Routing (Expo Router)
Relevant source files
The following files were used as context for generating this wiki page:The application utilizes expo-router, a file-system-based routing library for React Native and web applications. This system maps the file structure within the app/ directory to the navigation hierarchy of the app. It handles stack navigation, tabbed layouts, and deep linking, providing a declarative way to manage routes and navigation state.
The core of the navigation is built around a root stack layout that contains a primary tab-based navigation structure and dedicated routes for features like OAuth callbacks. Programmatic navigation between screens is handled via the useRouter hook.
Sources: package.json:20, app/_layout.tsx, app/(tabs)/_layout.tsx
File-based Routing Architecture
Expo Router uses the directory structure inside app/ to define the navigation tree.
app/_layout.tsx: Defines the root layout for the entire application.app/(tabs)/: A directory group for screens that are part of the main tab navigator. The parentheses indicate that(tabs)is not part of the URL path.app/(tabs)/_layout.tsx: Defines the layout for the tab navigator itself, including the tab bar and its screens.app/(tabs)/index.tsx: The default screen for the(tabs)group, corresponding to the "Home" tab.app/oauth/callback.tsx: A specific screen outside the tab group, used to handle OAuth deep link redirects.
This diagram illustrates the mapping from the file system to the app's route structure.
Root Layout
The root layout, defined in app/_layout.tsx, is the entry point of the app's UI. It wraps the entire application with necessary context providers and configures the top-level Stack navigator.
Key Responsibilities:
- Provider Setup: Wraps the app in
ThemeProvider,LanguageProvider,QueryClientProvider,trpc.Provider, andSafeAreaProviderto make global state and utilities available throughout the component tree. - Root Navigator: Initializes a
Stacknavigator. By default, headers are hidden (headerShown: false) to allow individual screens or nested navigators to control their own headers. - Screen Configuration: Defines the screens available at the root level.
The following screens are configured in the root stack:
| Screen Name | Description | Source File |
|---|---|---|
(tabs) | The main tab navigator containing the core app screens. | app/(tabs)/_layout.tsx |
oauth/callback | A dedicated screen to handle the redirect after OAuth authentication. | app/oauth/callback.tsx |
Sources: app/_layout.tsx:11-30, 80-83
// app/_layout.tsx:80-83
<Stack screenOptions={{ headerShown: false }}>
<Stack.Screen name="(tabs)" />
<Stack.Screen name="oauth/callback" />
</Stack>
The unstable_settings object is configured to set the initial route to the (tabs) group, meaning the user is first directed to the tab navigator on app launch.
// app/_layout.tsx:32-34
export const unstable_settings = {
anchor: "(tabs)",
};
Sources: app/_layout.tsx:32-34
Tab Navigation
The primary user interface is a tab bar defined in app/(tabs)/_layout.tsx. It uses the <Tabs> component from expo-router to create a bottom tab navigator.
Tab Screens
The following screens are configured as tabs:
| Name | Title | Icon (expo-symbols) |
|---|---|---|
index | Home | house.fill |
documents | Documents | paperplane.fill |
settings | Settings | chevron.left.forwardslash.chevron.right |
analyze | Analyze | paperplane.fill |
generate | Generate | paperplane.fill |
chat | Chat | paperplane.fill |
Sources: app/(tabs)/_layout.tsx:25-62
Configuration
The tab bar is customized with several options:
tabBarActiveTintColor: Sets the color for the active tab icon and label.headerShown: false: Hides the default header for all tab screens.tabBarButton: HapticTab: Uses a custom component to add haptic feedback on tab press.tabBarStyle: Custom styles are applied for padding, height, background color, and border.
Sources: app/(tabs)/_layout.tsx:14-23
Programmatic Navigation
Navigation between screens is performed programmatically using the useRouter hook from expo-router.
The router.push() method is used to navigate to a new screen. For example, on the Home screen, tapping the "Analyze Document" card navigates the user to the Analyze screen.
// app/(tabs)/index.tsx:61-64
<TouchableOpacity
onPress={() => router.push("/(tabs)/analyze")}
className="bg-surface border border-border rounded-lg p-4 active:opacity-70"
>
Sources: app/(tabs)/index.tsx:14, 62
Similarly, the Analyze screen can push to a modal for camera access. Although app/camera-modal.tsx is not provided, the navigation call implies its existence.
// app/(tabs)/analyze.tsx:43-45
const handleOpenCamera = () => {
router.push("/camera-modal");
};
Sources: app/(tabs)/analyze.tsx:44
The following diagram shows the sequence of a user navigating from the Home screen to the Analyze screen.
Sources: app/(tabs)/index.tsx:61-64
Deep Linking for OAuth
The routing system is configured to handle deep linking, which is essential for the OAuth authentication flow.
1. Scheme Configuration
The app's custom URL scheme is defined in app.config.ts. This scheme is dynamically generated from the project's bundleId to ensure uniqueness. It allows the app to be opened by a URL (e.g., manus20260105013345://oauth/callback).
Sources: app.config.ts:7-12, 19, 26
2. Redirect URI Generation
On native platforms, the redirectUri for the OAuth provider is created using Linking.createURL with the defined scheme. This ensures that after successful authentication in the browser, the provider redirects back to the app.
Sources: constants/oauth.ts:60-63
3. OAuth Callback Handling
The app/oauth/callback.tsx screen is the target for the OAuth redirect.
- It uses the
useLocalSearchParamshook to extract thecodeandstatequery parameters from the incoming deep link URL. - It performs the authentication logic (e.g., exchanging the code for a session token).
- Upon success, it uses
router.replace('/(tabs)')to redirect the user to the main application interface, replacing the callback screen in the navigation stack.
This flow is visualized in the diagram below.