← All recipes

Mobile Expo

Customize your project context

Your project

Prefer terminal? npx github:Pranavoro/tailormd init mobile-expo

Live preview — CLAUDE.md

# My App

TailorMD recipe: **Mobile Expo**

## Stack

- **Expo SDK:** SDK 52
- **Navigation:** Expo Router
- **State management:** Zustand
- **EAS channel:** production

## Folder map

```
app/               # Expo Router file-based routes (if Expo Router = Expo Router)
  (tabs)/
  (auth)/
src/
  components/      # reusable UI
  hooks/
  stores/          # Zustand state
  services/        # API clients
  utils/
assets/            # images, fonts
```

## Non-negotiables

- Test on both iOS and Android simulators before ship
- No secrets in client bundle — use env + EAS secrets
- Handle offline and slow-network states gracefully
- Respect platform conventions (safe areas, back gesture, haptics)
- Native modules require explicit approval — prefer Expo-managed packages
- OTA updates go to production channel only after QA pass

## Naming and style

- Components: `PascalCase.tsx`
- Hooks: `useCamelCase.ts`
- Screens/routes: `kebab-case` (Expo Router) or `PascalCase` (React Navigation)
- Store slices: descriptive nouns (`useAuthStore`)
- Assets: `kebab-case.png` / `.webp`

## Commands

```bash
npx expo start              # dev server
npx expo run:ios            # native iOS build
npx expo run:android        # native Android build
npm run test                # jest / vitest
npm run lint
eas build --profile preview # test build
eas update --channel production
```

## Ship checklist

Before merging or deploying:

- [ ] Runs on iOS and Android simulators
- [ ] No TypeScript errors
- [ ] Lint and tests pass
- [ ] No secrets in diff or client bundle
- [ ] Loading, error, and empty states handled
- [ ] Safe area and keyboard avoidance verified
- [ ] App icon and splash screen correct
- [ ] EAS build succeeds for target channel (production)

## Navigation (Expo Router)

- **Expo Router:** file-based routes in `app/`; use layouts for shared chrome
- **React Navigation:** typed param lists; no magic string route names

## State (Zustand)

- Server state via fetch/query layer; UI state in Zustand
- Avoid prop drilling — colocate state with the screen that owns it
- Persist auth tokens securely (SecureStore, not AsyncStorage)