Skip to main content

Expo Router Overview

Sovran uses Expo Router for navigation, which provides file-based routing similar to Next.js. Routes are defined by the file structure in the app/ directory. The app follows a three-level navigation structure:

Root Layout

app/_layout.tsx:1-334 Key responsibilities:
  • Initialize provider hierarchy (see Architecture Overview)
  • Configure Stack navigator with modal presentations
  • Handle theme changes via React key prop
  • Manage profile switching by remounting inner providers
Screen options:

Drawer Layout

app/(drawer)/_layout.tsx:1-454 The drawer provides the main app navigation with a custom drawer content component. Features:
  • Profile switcher at the top (switch between multiple wallets)
  • User avatar and QR code (tap to view profile)
  • Menu items: Feed, Wallet, Payments, Settings
  • 82% screen width (max 320px)
  • Slide animation with 60% black overlay
Profile switching: app/(drawer)/_layout.tsx:89-119 Menu structure:

Tab Layout

app/(drawer)/(tabs)/_layout.tsx:1-166 The tabs use conditional rendering based on device capabilities:

iOS 26+ with Liquid Glass

app/(drawer)/(tabs)/_layout.tsx:38-98 Uses Expo55NativeTabs for native iOS tab bar with blur effects.

Android & iOS (below version 26)

app/(drawer)/(tabs)/_layout.tsx:102-164 Fallback to standard Tabs with BlurView background. Tab structure:
  • Feed (feed/) - Social feed and stories (house icon)
  • Payments (payments/) - Contacts and messages (arrow.up.arrow.down icon)
  • Wallet (index/) - Balances and transactions (wallet.bifold icon)
  • Explore (explore/) - AI chat, maps, pending ecash (paperplane icon)
The wallet tab is at index/ (not wallet/) for backward compatibility with deep links.

Route Groups

Expo Router uses parentheses () for route groups that don’t appear in URLs:

Flow-Based Groups

These groups represent multi-step user flows:

Special Layouts

Each route group has a _layout.tsx that configures:
  • Presentation style (modal, formSheet, card)
  • Header options (title, background, blur)
  • Gesture handling
  • Animation settings
Example from send flow:

Type-Safe Navigation

The as any cast is required because Expo Router’s TypeScript types don’t fully support dynamic route groups yet. This is safe as long as the pathname string matches an actual route file.

Deep Linking

app.json configures deep link schemes:
Deep links are handled by useDeeplink hook:
app/_layout.tsx:216-220 Modal screens are configured in config/modalScreens.ts:

Route Parameters

Access route parameters using useLocalSearchParams:

Payment Flow Routing

The payment flows use intelligent routing based on available data: hooks/coco/useProcessPaymentString.ts:216-331 Payment request routing logic: This minimizes user friction by skipping screens when data is already available.

Screen Transitions

Sovran uses hero transitions for seamless animations between screens:
Example: Tapping the wallet health card on explore page transitions to full health modal with shared element animation.

Best Practices

Screens in app/ should be orchestration-only. Move business logic to hooks/, data fetching to stores/, and UI to components/blocks/.
When navigating from a form screen to another form screen, use router.replace() to avoid stack buildup:
Route parameters are always strings or undefined. Validate and parse them at the top of the component:
Always consider what happens when the user goes back. Use router.canGoBack() to check if there’s a previous screen:

State Management

Learn how Zustand stores integrate with navigation

Cashu Integration

See how payment flows interact with Coco manager