Project structure
├── index.ts # Custom entry: configures styles, then starts the router├── app.config.ts # Dynamic Expo config (name, bundle id, scheme, plugins)├── env.ts # Zod schema: validates env vars, builds typed config├── .env.example # The only committed env file; seeds the other three├── eas.json # EAS build profiles├── .eas/workflows/ # EAS workflows: build, build-fast, release├── scripts/ # setup-env.mjs, repack.mjs, eas-pre-install.sh├── .skills/ # Step-by-step guides for AI coding assistants└── src/ ├── app/ # Expo Router routes: one-line re-exports of feature screens │ ├── _layout.tsx # Root layout: bootstrap, providers, auth gating │ ├── (auth)/login.tsx # → LoginScreen │ └── (tabs)/ # index → Home, settings → Settings ├── features/ # One folder per feature │ ├── auth/ # screens/, store/, api/, hooks.ts, session.ts, types.ts │ │ └── index.ts # The feature's public API │ ├── home/ # HomeScreen (posts demo) │ ├── posts/ # api/: example query and mutation hooks │ └── settings/ # SettingsScreen (language picker, logout) ├── shared/ # Feature-agnostic building blocks │ ├── components/ # Button, TextField, ControlledTextField, ScreenWrapper, tab bar parts, AppOverlay │ ├── constants/ # Route names │ ├── hooks/ # useAppState, useDebounce, useBlockBackNavigation, useScreenLoader │ └── utils/ # Responsive scale helpers, toasts, app loader ├── providers/ # AppProviders + useAppBootstrap() ├── services/ # Axios client, QueryClient provider, pagination helpers ├── store/ # createPersistedStore(), rehydrateStores(), StoreProvider ├── storage/ # Encrypted MMKV + token storage ├── localization/ # i18n setup, translations, typed hooks ├── styles/ · theme/ # Themes, breakpoints, fonts, text styles └── assets/ # Fonts and imagesWith React Navigation, src/app/ is replaced by src/navigation/ (root stack, auth stack, main tabs, linking config and param-list types) and src/App.tsx.
Features own their code
Section titled “Features own their code”Everything for a feature lives in src/features/<feature>/:
| Folder / file | Contains |
|---|---|
screens/ |
The feature’s screens |
api/ |
react-query-kit hooks for its endpoints |
store/ |
Its state (Zustand store or Redux slice), private to the feature |
hooks.ts |
The hooks other code uses to read and change that state |
types.ts |
Its types |
index.ts |
The public API. The only file other code may import |
Import rules
Section titled “Import rules”- Outside a feature, import it only through its
index.ts:import { useLogin } from '@/features/auth'. ESLint rejects deep imports such as@/features/auth/store/useUserStore. - Inside a feature, use relative imports.
src/store/is the one exception: it wires every feature’s persisted store into start-up, so it may import stores directly.- Never import
axiosoutsidesrc/services/. Use the sharedclient.
Path aliases
Section titled “Path aliases”| Alias | Maps to | Example |
|---|---|---|
@/* |
src/* |
import { Button } from '@/shared/components' |
@env |
env.ts (a mock under Jest) |
import Env from '@env' |
Both are set in tsconfig.json and babel.config.js.
Start-up order
Section titled “Start-up order”Some things have to happen in order before the first screen renders. useAppBootstrap(), called from the root layout, handles it:
initStorage()reads the encryption key from the keychain and opens MMKV.rehydrateStores()loads persisted state, soisLoggedInis correct before anything renders.initI18n()reads the saved language from MMKV.- The splash screen hides and the navigator mounts the auth or the main group.
The custom index.ts entry loads the style configuration before expo-router/entry, because route modules create styles at import time and need the themes registered first.
Native folders aren’t committed
Section titled “Native folders aren’t committed”android/ and ios/ are generated by expo prebuild from app.config.ts and are in .gitignore:
- Native config differs per environment (app name, env values). Committing one environment’s native code would conflict with the others.
- Prebuild is deterministic, so anyone can regenerate identical native projects.
- The repo stays small.