Claude-skill-registry expo-sdk
Expo SDK 54+ platform patterns. Use when configuring Expo apps, setting up root layouts, using expo-image, expo-haptics, safe areas, bottom sheets, FlashList, or StatusBar.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/expo-sdk" ~/.claude/skills/majiayu000-claude-skill-registry-expo-sdk && rm -rf "$T"
skills/data/expo-sdk/SKILL.mdExpo SDK
Overview
Expo SDK 54+ provides a managed React Native development environment with file-based routing (Expo Router), native module access, and streamlined build tooling. This skill covers app configuration, the root layout provider pattern, and key Expo/RN libraries.
Prerequisite:
npx create-expo-app or Expo SDK 54+ in package.json
Workflows
Setting up a new Expo demo:
- Create project:
npx create-expo-app [demo-name] --template blank-typescript - Install core dependencies:
pnpm add expo-router expo-image expo-haptics react-native-reanimated react-native-gesture-handler react-native-safe-area-context @gorhom/bottom-sheet @shopify/flash-list lucide-react-native nativewind tailwindcss@3 - Configure NativeWind (see
skill)nativewind - Set up root layout with provider stack
- Configure
with scheme, name, splashapp.json - Add route groups and screens
- Run:
(Expo dev server)pnpm start
Adding a new library:
- Install with pnpm:
pnpm add [library] - Check if Expo config plugin needed in
app.json - Rebuild dev client if native module added:
npx expo prebuild
Guidance
app.json Configuration
Key fields for demo apps:
| Field | Purpose |
|---|---|
| Display name |
| URL-safe identifier |
| Deep link scheme (e.g., ) |
| (default for demos) |
| Splash screen configuration |
| iOS bundle ID |
| Android package name |
| Expo config plugins (e.g., ) |
Root Layout Provider Pattern
The root
app/_layout.tsx wraps the entire app with providers. Standard order:
GestureHandlerRootView (flex: 1) └── SafeAreaProvider └── ThemeProvider / Context └── Stack (Expo Router)
must be outermost (required by gesture handler and bottom sheets)GestureHandlerRootView
provides safe area insets to all descendantsSafeAreaProvider- App-level context providers go between SafeAreaProvider and Stack
for custom headers<Stack screenOptions={{ headerShown: false }} />
expo-image (replaces RN Image)
Use
expo-image for all image rendering — provides caching, blurhash placeholders, content-fit modes, and animated transitions.
Key props:
— URI string or require() for local imagessource
— blurhash string for loading stateplaceholder
—contentFit
|'cover'
|'contain''fill'
— fade-in duration in ms (e.g.,transition
)300
expo-haptics
Provide tactile feedback on interactions:
— light tap for selections, togglesHaptics.selectionAsync()
— button press, card tapHaptics.impactAsync(ImpactFeedbackStyle.Medium)
— action completionHaptics.notificationAsync(NotificationFeedbackType.Success)
Use sparingly — haptics on every touch is annoying.
Safe Area Insets
Account for device notch, status bar, and home indicator:
— returnsuseSafeAreaInsets()
in points{ top, bottom, left, right }- Apply to screen containers:
paddingTop: insets.top - NativeWind classes: use
or wrap in SafeAreaViewpt-[${insets.top}px]
@gorhom/bottom-sheet
Replaces Radix Dialog for mobile modal patterns:
- Use for detail views, selections, filters, forms
- Define snap points:
snapPoints={['25%', '50%', '90%']} - Backdrop:
with press-to-dismissbackdropComponent
for scrollable content inside sheetsBottomSheetScrollView- Requires
as ancestorGestureHandlerRootView
FlashList (replaces FlatList)
High-performance list rendering from
@shopify/flash-list:
- Drop-in FlatList replacement with mandatory
propestimatedItemSize
— estimated height of each item in pointsestimatedItemSize={80}- Recycling architecture for smooth 60fps scrolling
- Use
for NativeWind stylingcontentContainerClassName
lucide-react-native
Icon library for React Native (matches web lucide-react):
- Import individual icons:
import { Home, Settings, ChevronRight } from 'lucide-react-native' - Props:
,size
,colorstrokeWidth - Consistent icon set across mobile and web codebases
StatusBar
Configure status bar appearance per screen:
for light backgrounds<StatusBar style="dark" />
for dark backgrounds<StatusBar style="light" />- Import from
expo-status-bar
Best Practices
- Wrap root layout in
withGestureHandlerRootViewstyle={{ flex: 1 }} - Use expo-image for all images (caching, blurhash, performance)
- Add haptics to primary actions only (buttons, major selections) — not every touch
- Set
on all FlashList componentsestimatedItemSize - Place providers in root
, not in individual screens_layout.tsx - Use
for manual padding,useSafeAreaInsets()
for simple wrappingSafeAreaView - Test on real device for haptics and performance verification
Anti-Patterns
- Using React Native
instead ofImageexpo-image - Using
for large datasets instead ofFlatListFlashList - Forgetting
(causes bottom sheet and gesture crashes)GestureHandlerRootView - Overusing haptics on every interaction
- Hardcoding status bar height instead of using safe area insets
- Missing
on FlashList (required prop, console warning)estimatedItemSize - Placing
inside ScrollView (causes layout issues)SafeAreaView - Not including
plugin inexpo-router
plugins arrayapp.json