Skip to content
Skip to content
Goodspeed

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.

ItemDescriptionStrength
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 timeExpo 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 genericsDynamic 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 guardsapp/(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 notificationsgasConfig.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 Linksapp.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 screenExpo 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.

  1. 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>
      );
    }
GDaily Allergens

Today's log

Gluten
Tree nuts
Shellfish
Dairy
HomeScanLogProfile

START WITH EXPO ROUTER

Your Expo Router app, generated