expo-react-native

zzoohub's avatarfrom zzoohub

Expo/React Native project conventions - structure, headless patterns, design tokens, and library combinations. Use when building cross-platform mobile apps with Expo.

⭐0stars🔀0forks📁View on GitHub🕐Updated Jan 10, 2026⬇️2downloads

When & Why to Use This Skill

This Claude skill provides a comprehensive architectural framework and best practices for building high-performance cross-platform mobile applications using Expo and React Native. It enforces Feature-Sliced Design (FSD) principles, headless logic patterns, and a curated high-performance tech stack—including TanStack Query, Zustand, and MMKV—to ensure mobile codebases are scalable, maintainable, and secure.

Use Cases

  • Case 1: Implementing a scalable project structure using Feature-Sliced Design (FSD) to organize complex Expo applications into clear layers like entities, features, and widgets.
  • Case 2: Refactoring bloated React Native components by applying headless patterns, separating complex business logic into custom hooks for better readability and reuse.
  • Case 3: Optimizing mobile UI performance by migrating from standard FlatLists to FlashList and implementing Reanimated for 60fps animations.
  • Case 4: Establishing secure data persistence and state management workflows using MMKV for speed and SecureStore for sensitive authentication tokens.
  • Case 5: Conducting architectural reviews to ensure proper dependency rules between app layers and preventing circular dependencies in large-scale mobile projects.
nameexpo-react-native
description|
Use whenbuilding cross-platform mobile apps with Expo, React Native screens, mobile-specific logic.
Do not use forUX decisions (use ux-design), token/component design (use design-system), web-only features.
Workflowux-design → design-system → this skill (mobile integration).

Expo & React Native Patterns

For latest APIs, use context7 MCP server with library-id facebook/react-native or expo/expo .

Package manager: Use bun for all commands.


Project Structure (Feature-Sliced Design)

Use Context7 MCP server with websites/feature-sliced_github_io

app/                 # Expo Router (file-based routing)
├── _layout.tsx      # Root layout
├── index.tsx        # Home (/)
└── some-page/
    └── index.tsx    # /some-page (routing + page composition)
src/
├── app/             # App-wide settings, providers, global styles
│   └── providers/
├── widgets/         # Large composite blocks (Header, Sidebar, Feed)
├── features/        # User interactions (auth, send-comment, add-to-cart)
│   └── auth/
│       ├── ui/
│       ├── model/
│       └── api/
├── entities/        # Business entities (user, product, order)
│   └── user/
│       ├── ui/
│       ├── model/
│       └── api/
└── shared/          # Reusable infrastructure
    ├── ui/          # Design system
    ├── lib/         # Utilities, helpers
    ├── api/         # API client
    └── config/      # Environment, constants

FSD Layer Rules

Layer Can import from Cannot import from
app All layers below -
widgets features, entities, shared app
features entities, shared app, widgets
entities shared app, widgets, features
shared - All layers above

Rule: Layers can only import from layers below. Never above.


Preferred Stack

Category Choice Why
State (server) TanStack Query Caching, background sync
State (client) Zustand Simple, no boilerplate
Storage MMKV 10x faster than AsyncStorage
Forms TanStack Form + Zod Type-safe, good validation
Lists FlashList Recycling, better perf than FlatList
Animation Reanimated + Gesture Handler 60fps, runs on UI thread

Headless Patterns

Rule: Separate WHAT (logic) from HOW (presentation).

// ❌ Before: 800-line single file with 15 useStates
const DiaryHistory = () => {
  const [meals, setMeals] = useState([]);
  const [isLoading, setIsLoading] = useState(true);
  // ... 500+ lines
};
// ✅ After: Main component is composition only
const DiaryHistory = () => {
  const { range, setRange } = useDateRange();
  const { meals, isLoading, loadMore } = useMealHistory({ range });
  const { sections } = useSortedMeals(meals);

  return (
    <Screen>
      <FilterBar range={range} onRangeChange={setRange} />
      <MealSectionList sections={sections} onEndReached={loadMore} />
    </Screen>
  );
};

Rule: Main component does composition only. Logic goes in hooks.

Hook Interface Guidelines

// ✅ Clean, predictable return
const { data, isLoading, error, refetch } = useQuery();
const { selected, toggle, clear } = useSelection();

Performance Rules

Rule Why
FlashList over FlatList Recycling, handles 10k+ items
Memoize components with callbacks Prevent unnecessary re-renders
useCallback for handlers passed down Stable references
InteractionManager.runAfterInteractions Defer heavy work until after navigation
Test on physical device Simulators hide real performance issues

Rule: If it scrolls, use FlashList. If it's slow, profile on real device.


Quick Checklist

Architecture

  • Main component is composition only (no 15 useStates)
  • Logic extracted to custom hooks
  • Hook interfaces are clean and predictable

Performance

  • Using FlashList for lists (not FlatList)
  • Components memoized where needed
  • Heavy work deferred with InteractionManager
  • Tested on physical device (not just simulator)

State & Data

  • Server state in TanStack Query
  • Client state in Zustand
  • Persistent state uses MMKV (not AsyncStorage)

Platform

  • Works on both iOS and Android
  • Safe areas handled
  • Keyboard doesn't cover inputs

Design System

  • Using tokens from design-system (no hardcoded values)
  • Touch targets 44pt+

Security Configuration

Item Value
Token storage SecureStore (not AsyncStorage)
JWT access token 1 hour
JWT refresh token 1 year
Certificate pinning Required for sensitive APIs