Build apps with Expo Router
Every Goodspeed app uses Expo Router 55.x (Expo SDK 55, React Native 0.83): file-system routes under app/, typed links validated at build time, nested layouts, and deep links wired from day one. Goodspeed generates Expo Router as a standard part of every app, so the output is a working codebase from day one, not a scaffold you have to finish yourself.
WHAT GETS GENERATED
Built into every Expo Router build
Every app Goodspeed generates with Expo Router includes these production-ready patterns, wired together from the first build.
| Item | Description | Strength |
|---|---|---|
| File-system route tree under app/ | The generated app/ directory follows Expo Router conventions: app/(tabs)/ for the bottom tab bar, app/(modal)/ for sheets and paywalls, app/[id].tsx for detail screens, and app/_layout.tsx as the root provider. Each file is a route with no manual route registration. | Architecture |
| Typed links with href validation at build time | Expo Router's typed routes feature (expo-router ~55.0.14 with 'expo.experiments.typedRoutes': true) generates a RouteParamList type from the file tree. The href prop on Link and router.push() reject invalid paths at compile time, not runtime. | Type Safety |
| useLocalSearchParams with typed generics | Dynamic routes like app/items/[id].tsx use useLocalSearchParams<{ id: string }>() so the param is typed at the call site. No casting or runtime string coercion. | Type Safety |
| Nested layouts for auth guards | app/(auth)/_layout.tsx wraps the authenticated route group. The layout checks the Supabase session and calls router.replace('/login') if the user is signed out, without adding redirect logic to every screen. | Auth |
| Deep linking from OAuth, email, and push notifications | gasConfig.app.scheme registers the custom URL scheme used for OAuth redirect URIs, Supabase magic-link callbacks, and notification taps. Expo Router reads the linking config automatically so every deep-link opens the correct screen. | Deep Links |
| Apple Universal Links and Android App Links | app.config.js wires the associatedDomains and intentFilters for the generated bundle ID, enabling https://yourdomain.com/items/123 to open directly in the app without a custom-scheme fallback. | Deep Links |
| Route-level error boundaries and not-found screen | Expo Router wraps every route in an error boundary from ErrorBoundary. app/+not-found.tsx handles unmatched paths with a branded fallback screen instead of a blank crash. | Reliability |
Source: gas-template repository · File-based Routing
REAL GENERATED CODE
A snippet from a Expo Router app the studio shipped
This pattern comes directly from the gas-template codebase, the foundation every Goodspeed app is generated on. The studio generates Expo Router code like this for every app in the pipeline, not just a hello-world scaffold.
Typed navigation
// app/items/[id].tsx: typed dynamic route import { useLocalSearchParams, router } from 'expo-router'; export default function ItemDetail() { // id is typed as string, no casting needed const { id } = useLocalSearchParams<{ id: string }>(); return ( <View> <Text>Item {id}</Text> {/* href is validated at compile time via typed routes */} <Link href={{ pathname: '/items/[id]', params: { id } }}> Share </Link> </View> ); }
Today's log