react-native · alinaqi — Claude Code skill: mobile app patterns
A skill that sets project structure, component patterns and testing rules for React Native. Kicks in when the agent touches .tsx files or iOS and Android code.
npx skills add alinaqi/maggy --skill react-native -a claude-codeWhat react-native does
The skill activates on **/*.tsx, **/*.jsx, ios/, android/ and app.json. It lays out a project layout: pure business logic under src/core/, reusable components, screens paired with their own hooks, plus navigation, store and utils folders.
What it actually enforces is code style. Functional components only, logic pulled into hooks, props interfaces written out even when small. Local state with useState, global state with Zustand, server state with React Query. Component and hook tests go through React Native Testing Library.
For platform differences it suggests Platform.select, and separate Component.ios.tsx and Component.android.tsx files when the gap is bigger. There is also an anti-pattern list: inline styles, logic in render, list index as key, direct state mutation. You never invoke it by hand — the path match does the work.
What it helps with
- split a screen into a presentational component and a logic hook
- move iOS and Android differences into separate files
- test a component for press handling and disabled state
- switch server data to React Query with cache invalidation
- drop inline styles and anonymous props functions
Example prompts for the agent
- Split HomeScreen into a component and a useHome hook, keep logic out of render
- Add a shadow with Platform.select for iOS and Android
- Write a Button test: press calls onPress, disabled does not
How to install react-native
1Skills CLI (Vercel)
npx skills add alinaqi/maggy --skill react-native -a claude-code2Manually: put the files in place
~/.claude/skills/react-native/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/react-native/
1Skills CLI (Vercel)
npx skills add alinaqi/maggy --skill react-native -a codex2Manually: put the files in place
.agents/skills/react-native/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add alinaqi/maggy --skill react-native -a cursor2Manually: put the files in place
.cursor/skills/react-native/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install alinaqi/maggy react-native2Manually: put the files in place
.github/skills/react-native/SKILL.md1Skills CLI (Vercel)
npx skills add alinaqi/maggy --skill react-native -a gemini-cli2Platform CLI
gemini skills install https://github.com/alinaqi/maggy --path skills/react-nativeManually: put the files in place
.devin/skills/react-native/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add alinaqi/maggy --skill react-native -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about react-native
How do I install react-native in Claude?
Run npx skills add alinaqi/maggy --skill react-native -a claude-code. The skill file lands at ~/.claude/skills/react-native/SKILL.md.
What does the react-native skill do?
It gives the agent a project structure and React Native patterns: functional components, hooks for logic, Zustand and React Query for state, tests via React Native Testing Library.
When does the skill activate on its own?
It is bound to **/*.tsx, **/*.jsx, ios/**, android/** and app.json. There is no manual call.
Source text
SKILL.md · MIT
---
name: react-native
description: React Native mobile patterns, platform-specific code
when-to-use: When working on React Native mobile app code
user-invocable: false
paths: ["**/*.tsx", "**/*.jsx", "ios/**", "android/**", "app.json"]
effort: medium
---
# React Native Skill
---
## Project Structure
```
project/
├── src/
│ ├── core/ # Pure business logic (no React)
│ │ ├── types.ts
│ │ └── services/
│ ├── components/ # Reusable UI components
│ │ ├── Button/
│ │ │ ├── Button.tsx
│ │ │ ├── Button.test.tsx
│ │ │ └── index.ts
│ │ └── index.ts # Barrel export
│ ├── screens/ # Screen components
│ │ ├── Home/
│ │ │ ├── HomeScreen.tsx
│ │ │ ├── useHome.ts # Screen-specific hook
│ │ │ └── index.ts
│ │ └── index.ts
│ ├── navigation/ # Navigation configuration
│ ├── hooks/ # Shared custom hooks
│ ├── store/ # State management
│ └── utils/ # Utilities
├── __tests__/
├── android/
├── ios/
└── CLAUDE.md
```
---
## Component Patterns
### Functional Components Only
```typescript
// Good - simple, testable
interface ButtonProps {
label: string;
onPress: () => void;
disabled?: boolean;
}
export function Button({ label, onPress, disabled = false }: ButtonProps): JSX.Element {
return (
<Pressable onPress={onPress} disabled={disabled}>
<Text>{label}</Text>
</Pressable>
);
}
```
### Extract Logic to Hooks
```typescript
// useHome.ts - all logic here
export function useHome() {
const [items, setItems] = useState<Item[]>([]);
const [loading, setLoading] = useState(false);
const refresh = useCallback(async () => {
setLoading(true);
const data = await fetchItems();
setItems(data);
setLoading(false);
}, []);
return { items, loading, refresh };
}
// HomeScreen.tsx - pure presentation
export function HomeScreen(): JSX.Element {
const { items, loading, refresh } = useHome();
return (
<ItemList items={items} loading={loading} onRefresh={refresh} />
);
}
```
### Props Interface Always Explicit
```typescript
// Always define props interface, even if simple
interface ItemCardProps {
item: Item;
onPress: (id: string) => void;
}
export function ItemCard({ item, onPress }: ItemCardProps): JSX.Element {
...
}
```
---
## State Management
### Local State First
```typescript
// Start with useState, escalate only when needed
const [value, setValue] = useState('');
```
### Zustand for Global State (if needed)
```typescript
// store/useAppStore.ts
import { create } from 'zustand';
interface AppState {
user: User | null;
setUser: (user: User | null) => void;
}
export const useAppStore = create<AppState>((set) => ({
user: null,
setUser: (user) => set({ user }),
}));
```
### React Query for Server State
```typescript
// hooks/useItems.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
export function useItems() {
return useQuery({
queryKey: ['items'],
queryFn: fetchItems,
});
}
export function useCreateItem() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: createItem,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['items'] });
},
});
}
```
---
## Testing
### Component Testing with React Native Testing Library
```typescript
import { render, fireEvent } from '@testing-library/react-native';
import { Button } from './Button';
describe('Button', () => {
it('calls onPress when pressed', () => {
const onPress = jest.fn();
const { getByText } = render(<Button label="Click me" onPress={onPress} />);
fireEvent.press(getByText('Click me'));
expect(onPress).toHaveBeenCalledTimes(1);
});
it('does not call onPress when disabled', () => {
const onPress = jest.fn();
const { getByText } = render(<Button label="Click me" onPress={onPress} disabled />);
fireEvent.press(getByText('Click me'));
expect(onPress).not.toHaveBeenCalled();
});
});
```
### Hook Testing
```typescript
import { renderHook, act } from '@testing-library/react-hooks';
import { useCounter } from './useCounter';
describe('useCounter', () => {
it('increments counter', () => {
const { result } = renderHook(() => useCounter());
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});
});
```
---
## Platform-Specific Code
### Use Platform.select Sparingly
```typescript
import { Platform } from 'react-native';
const styles = StyleSheet.create({
shadow: Platform.select({
ios: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
},
android: {
elevation: 2,
},
}),
});
```
### Separate Files for Complex Differences
```
Component/
├── Component.tsx # Shared logic
├── Component.ios.tsx # iOS-specific
├── Component.android.tsx # Android-specific
└── index.ts
```
---
## React Native Anti-Patterns
- ❌ Inline styles - use StyleSheet.create
- ❌ Logic in render - extract to hooks
- ❌ Deep component nesting - flatten hierarchy
- ❌ Anonymous functions in props - use useCallback
- ❌ Index as key in lists - use stable IDs
- ❌ Direct state mutation - always use setter
- ❌ Mixing business logic with UI - keep core/ pure
- ❌ Ignoring TypeScript errors - fix them
- ❌ Large components - split into smaller pieces
© alinaqi · MIT · alinaqi/maggy
Similar skills
Everything for Mobile →react-native by other authors comes first.