---
name: react-native-design
description: "React Native and Expo patterns for styling, Reanimated animations, navigation, native modules, and offline sync. Use when building cross-platform mobile apps, implementing navigation, creating animations, integrating native capabilities, setting up EAS builds, or optimizing React Native performance."
enabled: false
source: github:JuanJoseGonGi/skills
imported-from: github:JuanJoseGonGi/skills
---

# React Native Design & Architecture

Comprehensive patterns for React Native: styling, Reanimated 3, navigation (React Navigation + Expo Router), Expo project architecture, native modules, offline-first data, and EAS build/submit.

## When to Use This Skill

- Building cross-platform mobile apps with React Native or Expo
- Implementing navigation (React Navigation 6+ or Expo Router)
- Creating performant animations with Reanimated 3 and Gesture Handler
- Architecting Expo projects with proper structure
- Integrating native modules (haptics, biometrics, notifications)
- Building offline-first mobile applications
- Setting up EAS Build, Submit, and OTA updates
- Styling with StyleSheet, dynamic styles, and responsive layouts
- Optimizing list performance with FlashList

> **Reference files** in `references/` contain extended code examples:
> - `styling-patterns.md` — advanced StyleSheet, theming, responsive design
> - `navigation-patterns.md` — deep linking, nested navigators, auth flows
> - `reanimated-patterns.md` — layout animations, shared transitions, worklets
> - `architecture-patterns.md` — Expo Router, auth, offline-first, native modules, EAS

## Expo vs Bare React Native

| Feature            | Expo (recommended) | Bare RN        |
| ------------------ | ------------------ | -------------- |
| Setup complexity   | Low                | High           |
| Native modules     | EAS Build          | Manual linking |
| OTA updates        | Built-in           | Manual setup   |
| Build service      | EAS                | Custom CI      |
| Custom native code | Config plugins     | Direct access  |

## Quick Start (Expo)

```bash
npx create-expo-app@latest my-app -t expo-template-blank-typescript
npx expo install expo-router expo-status-bar react-native-safe-area-context
npx expo install @react-native-async-storage/async-storage
npx expo install expo-secure-store expo-haptics
```

## Core Patterns

### 1. StyleSheet and Dynamic Styles

```typescript
import { StyleSheet, View, Text, Platform } from 'react-native';

interface CardProps {
  variant: 'primary' | 'secondary';
  disabled?: boolean;
}

function Card({ variant, disabled }: CardProps) {
  return (
    <View style={[
      styles.card,
      variant === 'primary' ? styles.primary : styles.secondary,
      disabled && styles.disabled,
    ]}>
      <Text style={styles.title}>Title</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  card: {
    padding: 16,
    borderRadius: 12,
    ...Platform.select({
      ios: { shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4 },
      android: { elevation: 4 },
    }),
  },
  primary: { backgroundColor: '#6366f1' },
  secondary: { backgroundColor: '#f3f4f6', borderWidth: 1, borderColor: '#e5e7eb' },
  disabled: { opacity: 0.5 },
  title: { fontSize: 24, fontWeight: '600', color: '#1a1a1a' },
});
```

### 2. Flexbox Layout

```typescript
const styles = StyleSheet.create({
  column:       { flexDirection: 'column', gap: 12 },
  row:          { flexDirection: 'row', alignItems: 'center', gap: 8 },
  spaceBetween: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' },
  centered:     { flex: 1, justifyContent: 'center', alignItems: 'center' },
  fill:         { flex: 1 },
});
```

### 3. Navigation — React Navigation

```typescript
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

type RootStackParamList = {
  Home: undefined;
  Detail: { itemId: string };
};

const Stack = createNativeStackNavigator<RootStackParamList>();

function AppNavigator() {
  return (
    <NavigationContainer>
      <Stack.Navigator screenOptions={{
        headerStyle: { backgroundColor: '#6366f1' },
        headerTintColor: '#fff',
      }}>
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Detail" component={DetailScreen}
          options={({ route }) => ({ title: `Item ${route.params.itemId}` })} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}
```

### 4. Navigation — Expo Router

```typescript
// app/_layout.tsx
import { Stack } from 'expo-router'

export default function RootLayout() {
  return (
    <Stack screenOptions={{ headerShown: false }}>
      <Stack.Screen name="(tabs)" />
      <Stack.Screen name="(auth)" />
      <Stack.Screen name="modal" options={{ presentation: 'modal' }} />
    </Stack>
  )
}

// Programmatic navigation
import { router } from 'expo-router'
router.push('/profile/123')
router.replace('/login')
router.push({ pathname: '/product/[id]', params: { id: '123' } })
```

> Full Expo Router tabs, dynamic routes, and auth flow examples in `references/architecture-patterns.md`.

### 5. Reanimated 3 Animations

```typescript
import Animated, {
  useSharedValue, useAnimatedStyle, withSpring,
} from 'react-native-reanimated';

function AnimatedBox() {
  const scale = useSharedValue(1);
  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ scale: scale.value }],
  }));

  return (
    <Pressable onPress={() => {
      scale.value = withSpring(1.2, {}, () => { scale.value = withSpring(1) });
    }}>
      <Animated.View style={[styles.box, animatedStyle]} />
    </Pressable>
  );
}
```

### 6. Gesture Handler + Reanimated

```typescript
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, { useSharedValue, useAnimatedStyle, withSpring } from 'react-native-reanimated';

function DraggableCard() {
  const translateX = useSharedValue(0);
  const translateY = useSharedValue(0);

  const gesture = Gesture.Pan()
    .onUpdate((e) => {
      translateX.value = e.translationX;
      translateY.value = e.translationY;
    })
    .onEnd(() => {
      translateX.value = withSpring(0);
      translateY.value = withSpring(0);
    });

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ translateX: translateX.value }, { translateY: translateY.value }],
  }));

  return (
    <GestureDetector gesture={gesture}>
      <Animated.View style={[styles.card, animatedStyle]}>
        <Text>Drag me!</Text>
      </Animated.View>
    </GestureDetector>
  );
}
```

### 7. Animated Pressable Component

```typescript
import Animated, { useSharedValue, useAnimatedStyle, withSpring } from 'react-native-reanimated';
import { Pressable, Platform } from 'react-native';
import * as Haptics from 'expo-haptics';

const AnimatedPressable = Animated.createAnimatedComponent(Pressable);

export function Button({ title, onPress, variant = 'primary', disabled = false }: ButtonProps) {
  const scale = useSharedValue(1);
  const animatedStyle = useAnimatedStyle(() => ({ transform: [{ scale: scale.value }] }));

  return (
    <AnimatedPressable
      onPress={onPress}
      onPressIn={() => {
        scale.value = withSpring(0.95);
        if (Platform.OS !== 'web') Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
      }}
      onPressOut={() => { scale.value = withSpring(1) }}
      disabled={disabled}
      style={[styles.button, styles[variant], disabled && styles.disabled, animatedStyle]}
    >
      <Text style={styles.text}>{title}</Text>
    </AnimatedPressable>
  );
}
```

### 8. Offline-First with React Query

```typescript
import { QueryClient } from '@tanstack/react-query'
import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client'
import { createAsyncStoragePersister } from '@tanstack/query-async-storage-persister'
import AsyncStorage from '@react-native-async-storage/async-storage'
import NetInfo from '@react-native-community/netinfo'
import { onlineManager } from '@tanstack/react-query'

// Sync online status with React Query
onlineManager.setEventListener((setOnline) =>
  NetInfo.addEventListener((state) => setOnline(!!state.isConnected))
)

const queryClient = new QueryClient({
  defaultOptions: {
    queries: { gcTime: 86400000, staleTime: 300000, retry: 2, networkMode: 'offlineFirst' },
    mutations: { networkMode: 'offlineFirst' },
  },
})

const persister = createAsyncStoragePersister({
  storage: AsyncStorage,
  key: 'REACT_QUERY_OFFLINE_CACHE',
})
```

> Full offline-first provider, optimistic mutations, and auth flow in `references/architecture-patterns.md`.

### 9. Native Module Integration

```typescript
// Haptics — wrap with platform check
import * as Haptics from 'expo-haptics'
export const haptics = {
  light:   () => Platform.OS !== 'web' && Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light),
  medium:  () => Platform.OS !== 'web' && Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium),
  success: () => Platform.OS !== 'web' && Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success),
  error:   () => Platform.OS !== 'web' && Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error),
}

// Biometrics
import * as LocalAuthentication from 'expo-local-authentication'
export async function authenticateWithBiometrics(): Promise<boolean> {
  const hasHw = await LocalAuthentication.hasHardwareAsync()
  if (!hasHw) return false
  const enrolled = await LocalAuthentication.isEnrolledAsync()
  if (!enrolled) return false
  const result = await LocalAuthentication.authenticateAsync({
    promptMessage: 'Authenticate to continue', fallbackLabel: 'Use passcode',
  })
  return result.success
}
```

> Push notification setup in `references/architecture-patterns.md`.

### 10. FlashList Performance

```typescript
import { FlashList } from '@shopify/flash-list'
import { memo, useCallback } from 'react'

const ProductItem = memo(({ item, onPress }: { item: Product; onPress: (id: string) => void }) => (
  <Pressable onPress={() => onPress(item.id)} style={styles.item}>
    <Text>{item.name}</Text>
  </Pressable>
))

export function ProductList({ products, onPress }: Props) {
  return (
    <FlashList
      data={products}
      renderItem={useCallback(({ item }) => <ProductItem item={item} onPress={onPress} />, [onPress])}
      estimatedItemSize={100}
      removeClippedSubviews={true}
    />
  )
}
```

## EAS Build & Submit

```bash
# Build
eas build --platform ios --profile development
eas build --platform all --profile production

# Submit to stores
eas submit --platform ios
eas submit --platform android

# OTA update
eas update --branch production --message "Bug fixes"
```

> Full `eas.json` configuration in `references/architecture-patterns.md`.

## Best Practices

**Do:**
- Use Expo for faster development and OTA updates
- Use TypeScript with typed navigation params
- Use `StyleSheet.create` — never inline styles in render
- Memoize list items with `React.memo` and `useCallback`
- Use FlashList over FlatList for long lists
- Run animations on UI thread with Reanimated worklets
- Handle safe areas with `useSafeAreaInsets`
- Test on real devices — simulators miss real-world issues
- Use `expo-secure-store` for tokens, never AsyncStorage

**Don't:**
- Don't fetch in render — use useEffect or React Query
- Don't ignore platform differences — test iOS and Android
- Don't store secrets in code — use environment variables
- Don't skip error boundaries — mobile crashes are unforgiving
- Don't use ScrollView + map for lists — use FlatList/FlashList

## Common Issues

| Issue | Solution |
|---|---|
| Gesture conflicts | Use `GestureDetector` with `simultaneousHandlers` |
| Navigation type errors | Define `ParamList` types for all navigators |
| Animation jank | Move to UI thread with `runOnUI` worklets |
| Memory leaks | Cancel animations and cleanup in useEffect |
| Font loading | Use `expo-font` with splash screen |
| Safe area | Test on notched devices (iPhone, Android cutouts) |

## Resources

- [Expo Documentation](https://docs.expo.dev/)
- [Expo Router](https://docs.expo.dev/router/introduction/)
- [React Native Documentation](https://reactnative.dev/)
- [React Navigation](https://reactnavigation.org/)
- [Reanimated](https://docs.swmansion.com/react-native-reanimated/)
- [Gesture Handler](https://docs.swmansion.com/react-native-gesture-handler/)
- [FlashList](https://shopify.github.io/flash-list/)
