Most Expo apps get sign-in working on the first day and get auth guards wrong for the next six months. The login screen appears, the token lands in secure storage, and everything looks fine until a user reopens the app from a push notification, lands on a protected screen with an expired session, and sees a flash of someone else's layout — or worse, a blank screen with no way back.
Expo Router gives you the primitives to handle all of this: protected routes, layout-level redirects, and splash screen control that waits for async session restore. The official authentication guide covers the happy path well. This post covers the four production cases that break real apps: guarding route groups instead of individual screens, restoring sessions before first render, redirecting deep links through sign-in without losing the destination, and surviving the token-refresh race on cold start.
Put the guard on the layout, not the screen
The most common mistake is checking authentication inside individual screens. That means every new screen needs its own check, the unauthenticated UI flashes for a frame before the redirect fires, and one forgotten screen ships an unprotected route to production.
Expo Router's protected routes move the check to the layout. A layout wraps a whole group of routes, so a single guard covers every screen inside it. The project structure looks like this: a sign-in route that stays public, and an (app) group whose layout requires a session.






