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: the Goodspeed generation pipeline · File-based Routing
REAL GENERATED CODE
A snippet from a Expo Router app the studio shipped
This pattern comes straight out of the production groundwork every Goodspeed app is generated on. The studio generates Expo Router code like this for every app in the pipeline, not a hello-world sample you have to grow into a real app.
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