The <Protected /> component allows you to declaratively hide routes based on a boolean condition. When the guard prop is false, wrapped screens are completely filtered out from the navigator - they won’t appear in the navigation state and cannot be navigated to.
This is useful for authentication flows, feature flags, role-based access, or any scenario where routes should be conditionally available.
import { Stack, Protected } from 'one'
import { useSession } from '~/features/auth/ctx'
export default function Layout() {
const { session } = useSession()
return (
<Stack>
<Stack.Screen name="login" options={{ title: 'Login' }} />
<Stack.Screen name="index" options={{ title: 'Home' }} />
<Protected guard={!!session}>
<Stack.Screen name="dashboard" options={{ title: 'Dashboard' }} />
<Stack.Screen name="settings" options={{ title: 'Settings' }} />
</Protected>
</Stack>
)
}
When session is null/undefined, the dashboard and settings routes don’t exist in the navigation tree. Attempts to navigate to them will be blocked.
<Protected /> works with Stack, Tabs, Drawer, and the base Navigator component:
import { Tabs, Protected } from 'one'
export default function TabLayout() {
const { isAdmin } = useUser()
return (
<Tabs>
<Tabs.Screen name="home" />
<Tabs.Screen name="profile" />
<Protected guard={isAdmin}>
<Tabs.Screen name="admin" />
</Protected>
</Tabs>
)
}
You can nest <Protected /> components. Routes are only shown when all parent guards are true:
<Stack>
<Stack.Screen name="public" />
<Protected guard={isLoggedIn}>
<Stack.Screen name="dashboard" />
<Protected guard={isPremium}>
<Stack.Screen name="premium-features" />
</Protected>
</Protected>
</Stack>
In this example:
public is always availabledashboard requires isLoggedIn to be truepremium-features requires both isLoggedIn AND isPremium to be trueThere are two main approaches to protecting routes:
| Approach | When to Use |
|---|---|
<Protected /> | Routes should not exist when unauthorized |
<Redirect /> | Routes exist but redirect unauthorized users |
Use <Protected /> when routes should be completely hidden - they won’t appear in tab bars, won’t be in the navigation history, and navigation attempts will be blocked.
Use <Redirect /> when you want to intercept navigation and send users to a login page. This is better for deep linking scenarios where you want to redirect to login, then back to the original destination.
You can combine both approaches:
export default function AppLayout() {
const { session, isLoading } = useSession()
if (isLoading) return null
if (!session) return <Redirect href="/login" />
return (
<Stack>
<Stack.Screen name="index" />
<Protected guard={session.role === 'admin'}>
<Stack.Screen name="admin" />
</Protected>
</Stack>
)
}
| Prop | Type | Description |
|---|---|---|
guard | boolean | When false, all wrapped screens are filtered out |
children | ReactNode | <Stack.Screen />, <Tabs.Screen />, or other <Protected /> elements |
Edit this page on GitHub.