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

_layout.tsx
+html.tsx
+not-found.tsx
todos.tsx
profile.tsx
account-security.tsx
notifications.tsx
permissions.tsx
refer.tsx
upload-sample.tsx
paywall.tsx
redeem-code.tsx
manage-subscription.tsx
onboarding.tsx
consent-gate.tsx
growth-gates.tsx
premium-gate.tsx
sign-in-gate.tsx
theme-toggle.tsx
animated-splash.tsx
offline-banner.tsx
auth.tsx
supabase.ts
query.tsx
purchases.tsx
entitlement.tsx
notifications.tsx
analytics.tsx
sentry.ts
theme.ts
theme-preference.tsx
toast.tsx
app.config.ts
eas.json
.env.example

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:

FilePurpose
app/+html.tsxCustom web document shell for pnpm web / static web output
app/+not-found.tsxTyped-routes fallback for unknown URLs
app/component/[slug].tsxComponent gallery detail route
app/(tabs)/home-paywall.tsxHidden tab-context paywall surface with href: null
app/(tabs)/home-redeem-code.tsxHidden tab-context redeem surface with href: null

Module boundaries

AreaPut code here
Screen compositionapp/<route>.tsx
Auth formscomponents/auth/
Reusable primitivescomponents/ui/
Cross-screen gatescomponents/*-gate.tsx or lib/*Provider when state is needed
Server-state callsSmall modules in lib/, then TanStack Query in screens/providers
Local preferencesProvider modules in lib/ backed by AsyncStorage, SecureStore, or web localStorage
i18n copylib/i18n/en.json and lib/i18n/zh.json
Showcase-only demo cataloglib/showcase/ and app/showcase/
Supabase schema/functionssupabase/migrations/ and supabase/functions/
Generated DB typestypes/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:types

Add 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.

On this page