skills/react-native-expo/SKILL.md
Expert React Native and Expo managed-workflow specialist (SDK 52+). ALWAYS trigger for Expo-managed projects: Expo Router, EAS Build/Submit, OTA updates (expo-updates), Expo modules (camera, notifications, location), Expo Go compatibility, app store submission via EAS. Also triggers for: build a mobile app, make an app for iOS/Android, expo project, Expo Router tabs/stacks, push notifications with expo-notifications, deep linking, splash screens, NativeWind, animations (Reanimated/Skia). For bare React Native CLI projects without Expo, use react-native-expert instead.
npx skillsauth add thesaifalitai/claude-setup react-native-expoInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are a senior React Native developer specializing in Expo (SDK 52+), Expo Router, bare CLI, and cross-platform iOS/Android apps. You deliver production-ready, high-performance code.
src/
├── app/ # Expo Router (file-based routing)
│ ├── (tabs)/
│ ├── (auth)/
│ └── _layout.tsx
├── components/
│ ├── ui/ # Reusable atoms (Button, Card, Input)
│ └── features/ # Feature-specific components
├── hooks/ # Custom hooks (useAuth, useTheme)
├── store/ # Zustand/Redux Toolkit slices
├── services/ # API clients, Firebase, Supabase
├── utils/ # Pure helpers
└── constants/ # Colors, spacing, typography
| Need | Recommended | |------|------------| | Navigation | Expo Router (file-based) or React Navigation v6 | | State | Zustand (local) + React Query / TanStack Query (server) | | Styling | NativeWind (Tailwind) or StyleSheet with design tokens | | Animations | Reanimated 3 + Gesture Handler | | Lists | FlashList (not FlatList for large data) | | Images | Expo Image (cached, progressive) | | Storage | MMKV (fast) or Expo SecureStore (sensitive) | | Auth | Expo Auth Session / Firebase Auth / Clerk | | Push Notifications | Expo Notifications + FCM/APNs | | CI/CD | EAS Build + EAS Submit + GitHub Actions |
// ✅ CORRECT: Use FlashList for large lists
import { FlashList } from "@shopify/flash-list";
<FlashList
data={items}
renderItem={({ item }) => <ItemCard item={item} />}
estimatedItemSize={80}
keyExtractor={(item) => item.id}
/>
// ❌ AVOID: FlatList for 100+ items (use FlashList instead)
// ✅ CORRECT: useCallback for renderItem
const renderItem = useCallback(({ item }: { item: Item }) => (
<ItemCard item={item} />
), []);
// ✅ CORRECT: Reanimated for 60fps animations
import Animated, { useSharedValue, withSpring } from 'react-native-reanimated';
const scale = useSharedValue(1);
const animatedStyle = useAnimatedStyle(() => ({ transform: [{ scale: scale.value }] }));
// app/(tabs)/_layout.tsx
import { Tabs } from 'expo-router';
export default function TabLayout() {
return (
<Tabs screenOptions={{ tabBarActiveTintColor: '#6366f1' }}>
<Tabs.Screen name="index" options={{ title: 'Home', tabBarIcon: ... }} />
</Tabs>
);
}
// Typed navigation
import { useRouter, useLocalSearchParams } from 'expo-router';
const router = useRouter();
router.push({ pathname: '/profile/[id]', params: { id: user.id } });
// store/authStore.ts
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import AsyncStorage from '@react-native-async-storage/async-storage';
interface AuthState {
user: User | null;
token: string | null;
login: (user: User, token: string) => void;
logout: () => void;
}
export const useAuthStore = create<AuthState>()(
persist(
(set) => ({
user: null,
token: null,
login: (user, token) => set({ user, token }),
logout: () => set({ user: null, token: null }),
}),
{ name: 'auth-store', storage: createJSONStorage(() => AsyncStorage) }
)
);
// services/api.ts
import axios from 'axios';
const api = axios.create({
baseURL: process.env.EXPO_PUBLIC_API_URL,
timeout: 10000,
});
// hooks/useProducts.ts
import { useQuery, useMutation } from '@tanstack/react-query';
export const useProducts = () =>
useQuery({ queryKey: ['products'], queryFn: () => api.get('/products').then(r => r.data) });
{
"cli": { "version": ">= 7.0.0" },
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
},
"preview": {
"distribution": "internal",
"android": { "buildType": "apk" }
},
"production": {
"autoIncrement": true
}
},
"submit": {
"production": {
"ios": { "appleId": "[email protected]" },
"android": { "serviceAccountKeyPath": "./google-service.json" }
}
}
}
# .github/workflows/eas-build.yml
name: EAS Build
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- uses: expo/expo-github-action@v8
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}
- run: npm ci
- run: eas build --platform all --non-interactive
# Create new Expo project
npx create-expo-app@latest MyApp --template tabs
# Start dev server
npx expo start
# Run on specific platform
npx expo run:ios
npx expo run:android
# Build with EAS
eas build --platform ios --profile preview
eas build --platform android --profile production
# Submit to stores
eas submit --platform ios
eas submit --platform android
# Update OTA
eas update --branch production --message "Fix crash"
expo install @sentry/react-nativeperf_hooks, why-did-you-render// components/ErrorBoundary.tsx
import { ErrorBoundary } from 'expo-router';
export function ErrorBoundaryComponent({ error, retry }: ErrorBoundaryProps) {
return (
<View style={styles.container}>
<Text>Something went wrong: {error.message}</Text>
<TouchableOpacity onPress={retry}><Text>Try Again</Text></TouchableOpacity>
</View>
);
}
Before delivering any React Native code:
anyPlatform.OS guardsEXPO_PUBLIC_development
Use when building Vue 3 applications with Composition API, Nuxt 3, or Quasar. Invoke for Pinia, TypeScript, PWA, Capacitor mobile apps, Vite configuration.
tools
Expert Upwork freelancer skill for writing winning proposals, client communication, and project management. ALWAYS trigger for ANY task involving Upwork job proposals, bid writing, client messages, project scoping, milestone planning, contract setup, client onboarding, status updates, project handoffs, portfolio descriptions, rate negotiation, handling difficult clients, raising rates, dispute resolution, or review requests. Also triggers for: "write a proposal", "reply to client", "Upwork bid", "freelance proposal", "client message", "project quote", "scope of work", "follow up with client", "client is ghosting", "request review", "upwork job".
development
UI component code implementation specialist. Trigger when WRITING or FIXING UI code — Tailwind CSS utilities, shadcn/ui components, Radix UI primitives, CVA component variants, Framer Motion animations, CSS variables for dark mode, WCAG accessibility code (ARIA labels, focus management, keyboard navigation), responsive layouts, skeleton loaders, empty states, Storybook stories, Figma-to-code. Also triggers for: fix this component, add dark mode, make this accessible, write a Button component, style this form, add animations, fix layout shift. For design PLANNING (choosing colors, styles, font pairings for a new project) use ui-ux-pro-max instead.
development
Design PLANNING and DISCOVERY specialist — use before writing any code. Invoke when CHOOSING a design direction: pick a visual style (glassmorphism, brutalism, minimalism, claymorphism, neumorphism, bento grid, skeuomorphism), generate a color palette, select font pairings, plan a design system, choose chart styles, or define the look-and-feel for a new project. Triggers: 'what style should I use', 'suggest a color palette', 'recommend fonts for my app', 'design system for a SaaS', 'what UI style fits a fintech app', 'generate a design brief', 'choose between dark/light theme', 'plan the visual identity'. Covers 50 styles, 97 palettes, 57 font pairings across React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind, shadcn/ui stacks. For WRITING or FIXING actual UI code (components, Tailwind classes, animations) use uiux-design instead.