Project Structure
Understand the Expo template folders and module boundaries.
The Expo template is a full-featured Expo starter, so the project shape is
closer to an app than a library: routes live in app/, reusable UI lives in
components/, service modules live in lib/, and Supabase backend artifacts
ship beside the client.
Tree
Route boundaries
app/_layout.tsx owns global providers, gates, the root Stack, and modal
presentation for paywall and redeem-code. app/(auth)/ is the signed-out
auth stack. app/(tabs)/ is the main shell with four visible tabs: Home,
Components, Showcase, and Me. Other app files are pushed screens opened from the
drawer, feature cards, deep links, or modals.
Expo Router special files are part of the contract:
| File | Purpose |
|---|---|
app/+html.tsx | Custom web document shell for pnpm web / static web output |
app/+not-found.tsx | Typed-routes fallback for unknown URLs |
app/component/[slug].tsx | Component gallery detail route |
app/(tabs)/home-paywall.tsx | Hidden tab-context paywall surface with href: null |
app/(tabs)/home-redeem-code.tsx | Hidden tab-context redeem surface with href: null |
Module boundaries
| Area | Put code here |
|---|---|
| Screen composition | app/<route>.tsx |
| Auth forms | components/auth/ |
| Reusable primitives | components/ui/ |
| Cross-screen gates | components/*-gate.tsx or lib/*Provider when state is needed |
| Server-state calls | Small modules in lib/, then TanStack Query in screens/providers |
| Local preferences | Provider modules in lib/ backed by AsyncStorage, SecureStore, or web localStorage |
| i18n copy | lib/i18n/en.json and lib/i18n/zh.json |
| Showcase-only demo catalog | lib/showcase/ and app/showcase/ |
| Supabase schema/functions | supabase/migrations/ and supabase/functions/ |
| Generated DB types | types/database.ts from pnpm gen:types |
lib/ is the de-facto service layer. Files like profile.ts,
sample-uploads.ts, referrals.ts, deep-links.ts, network.ts, and
app-update-config.ts keep feature logic out of route components.
Add a feature
Add backend shape first
If the feature stores data, create a migration in supabase/migrations/, push
it, and run:
pnpm gen:typesAdd a lib/ module
Keep Supabase calls, mapping, retry helpers, and URL building in lib/your-feature.ts.
Add queryKeys in lib/query.tsx when the feature uses TanStack Query.
Add screens and navigation
Create the route in app/. Add drawer entries in app/(tabs)/_layout.tsx,
feature cards on Home, or deep-link allow-list entries in lib/deep-links.ts
only when the route should be externally openable.
Add copy and reusable UI
Put translated product copy in both locale files. If the UI becomes reusable,
extract it to components/ or components/ui/ instead of importing route code
from another route.
