Documentation Index
Fetch the complete documentation index at: https://docs.mobile-starter.amisi.ai/llms.txt
Use this file to discover all available pages before exploring further.
@amisi/navigation
@amisi/navigation provides router helpers, navigation guards, and deep linking utilities for Expo Router applications.
Features
- Deep link configuration - Parse and build deep links with type safety
- Navigation guards - Protect routes based on auth, onboarding, subscription status
- Transition presets - Reusable stack navigation transitions
- Typed routes - Type-safe route helpers and navigation
Deep Linking
Create linking configuration
import { createLinkingConfig } from '@amisi/navigation';
const linking = createLinkingConfig({
scheme: 'myapp',
prefixes: ['https://myapp.com', 'myapp://'],
});
Parse deep links
import { parseDeepLink } from '@amisi/navigation';
const parsed = parseDeepLink('myapp://profile/123');
// { screen: 'Profile', params: { id: '123' } }
Build deep links
import { buildDeepLink } from '@amisi/navigation';
const url = buildDeepLink('myapp', 'Profile', { userId: '123' });
// 'myapp://profile?userId=123'
Check route types
import { isAuthenticatedRoute, isOnboardingRoute } from '@amisi/navigation';
if (isAuthenticatedRoute('Profile')) {
// Redirect to sign in if not authenticated
}
if (isOnboardingRoute('Welcome')) {
// Show onboarding flow
}
Navigation Guards
Guards prevent screen flashing and protect routes based on app state.
AuthGate
Protect authenticated routes and redirect unauthenticated users:
import { AuthGate } from '@amisi/navigation';
import { useAuthContext } from '@amisi/auth';
export default function RootLayout() {
const { session, loading } = useAuthContext();
return (
<AuthGate
isAuthenticated={!!session}
isLoading={loading}
fallback={<LoadingScreen />}
redirectTo="/sign-in"
>
<Stack />
</AuthGate>
);
}
OnboardingGate
Ensure users complete onboarding before accessing the app:
import { OnboardingGate } from '@amisi/navigation';
<OnboardingGate
hasCompletedOnboarding={user?.onboardingComplete}
isLoading={loading}
redirectTo="/onboarding"
>
<MainApp />
</OnboardingGate>;
SubscriptionGate
Require active subscription for premium features:
import { SubscriptionGate } from '@amisi/navigation';
import { useSubscriptions } from '@amisi/subscriptions';
export function PremiumFeature() {
const { status, loading } = useSubscriptions();
return (
<SubscriptionGate
hasActiveSubscription={status?.isActive ?? false}
isLoading={loading}
redirectTo="/subscription"
>
<PremiumContent />
</SubscriptionGate>
);
}
AppLockGate
Handle app lock/unlock state:
import { AppLockGate } from '@amisi/navigation';
import { useAppLock } from '@amisi/app-lock';
export function App() {
const { isLocked, loading, unlock } = useAppLock();
return (
<AppLockGate
isLocked={isLocked}
isLoading={loading}
onUnlock={unlock}
fallback={<SplashScreen />}
>
<MainApp />
</AppLockGate>
);
}
ComposedGate
Combine multiple guards:
import { ComposedGate } from '@amisi/navigation';
<ComposedGate
guards={[
{ condition: isAuthenticated, redirectTo: '/sign-in' },
{ condition: hasCompletedOnboarding, redirectTo: '/onboarding' },
{ condition: !isLocked, redirectTo: '/lock' },
]}
fallback={<LoadingScreen />}
>
<MainApp />
</ComposedGate>;
Transition Presets
Reusable navigation transitions for consistent animations.
Using presets
import { transitionPresets } from '@amisi/navigation';
<Stack.Screen
name="modal"
options={transitionPresets.modal}
/>
<Stack.Screen
name="auth"
options={transitionPresets.fade}
/>
Available presets
- modal - Standard modal presentation
- transparentModal - Modal with transparent background
- slideFromRight - Horizontal slide (default stack)
- slideFromBottom - Vertical slide
- fade - Fade in/out
- noAnimation - No animation
- auth - Fade transition for auth screens
- onboarding - Horizontal slide for onboarding
- tabBar - Fade for tab navigation
- platformModal - Platform-specific modal (iOS modal, Android slide)
- platformAuth - Platform-specific auth (iOS fade, Android slide)
import {
getTransitionForPlatform,
fadeTransition,
slideFromRightTransition,
} from '@amisi/navigation';
const customTransition = getTransitionForPlatform(
fadeTransition,
slideFromRightTransition,
);
Custom transitions
import { createCustomTransition } from '@amisi/navigation';
const myTransition = createCustomTransition({
gestureEnabled: true,
gestureDirection: 'horizontal',
animationEnabled: true,
});
Typed Routes
Type-safe route helpers and navigation.
Route constants
import { routes } from '@amisi/navigation';
// Navigate to routes
router.push(routes.home());
// '/'
router.push(routes.profile('user123'));
// '/profile/user123'
router.push(routes.details('item456'));
// '/details/item456'
Build routes with params
import { buildRoute } from '@amisi/navigation';
const path = buildRoute('/profile', { tab: 'settings', edit: true });
// '/profile?tab=settings&edit=true'
Parse routes
import { parseRoute } from '@amisi/navigation';
const { pathname, params } = parseRoute('/profile?tab=settings');
// { pathname: '/profile', params: { tab: 'settings' } }
Match routes with patterns
import { matchRoute } from '@amisi/navigation';
const { match, params } = matchRoute('/details/123', '/details/:id');
// { match: true, params: { id: '123' } }
Route type checking
import { isModalRoute, isAuthRoute, isProtectedRoute } from '@amisi/navigation';
if (isModalRoute('details')) {
// Use modal transition
}
if (isAuthRoute('signIn')) {
// Don't require authentication
}
if (isProtectedRoute('home')) {
// Require authentication
}
Get initial route
import { getInitialRoute } from '@amisi/navigation';
const initialRoute = getInitialRoute(isAuthenticated, hasCompletedOnboarding);
// Returns: 'SignIn' | 'Welcome' | 'Home'
Navigation State Helpers
import {
getActiveRouteName,
getPreviousRouteName,
canGoBack,
} from '@amisi/navigation';
const currentRoute = getActiveRouteName(navigationState);
const previousRoute = getPreviousRouteName(navigationState);
const canNavigateBack = canGoBack(navigationState);
Architecture Benefits
This package solves common navigation pain points:
- Deep link → correct stack - Parse and route deep links to the right screen
- Modal routes - Consistent modal presentation across platforms
- No screen flashing - Guards prevent showing wrong screens during state changes
- Type safety - Typed route helpers prevent navigation errors
- Reusable transitions - Consistent animations across the app
- Composable guards - Combine multiple protection layers easily
Example: Complete Setup
import { Stack } from 'expo-router';
import { AuthGate, OnboardingGate, transitionPresets } from '@amisi/navigation';
import { useAuthContext } from '@amisi/auth';
export default function RootLayout() {
const { session, loading } = useAuthContext();
return (
<AuthGate
isAuthenticated={!!session}
isLoading={loading}
fallback={<LoadingScreen />}
>
<OnboardingGate
hasCompletedOnboarding={session?.user?.onboardingComplete}
isLoading={loading}
>
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="modal" options={transitionPresets.modal} />
<Stack.Screen
name="details/[id]"
options={transitionPresets.slideFromRight}
/>
</Stack>
</OnboardingGate>
</AuthGate>
);
}