Expo Router v7 Gids 2026: Navigatie, Protected Routes en Deep Linking
Leer alles over Expo Router v7 in deze praktische Nederlandse gids. Van file-based routing en Stack.Protected authenticatie tot modale schermen, type-veilige navigatie en automatische deep linking in Expo SDK 55. Met werkende codevoorbeelden.
Navigatie is het kloppende hart van elke mobiele app. Dat klinkt misschien wat dramatisch, maar denk er eens over na — zonder goede navigatie raken gebruikers verdwaald, haken ze af en verwijderen ze je app sneller dan je "npm install" kunt typen. Met de release van Expo SDK 55 en Expo Router v7 is navigatie in React Native flink op de schop gegaan. Waar je vroeger handmatig navigators moest configureren, stacks moest nesten en deep links apart moest instellen, draait nu alles om je mappenstructuur.
Voeg een bestand toe aan de app/-map en je hebt een route — op iOS, Android én het web. Zo simpel is het.
In deze gids nemen we alles door wat je moet weten over Expo Router v7: van de basis van file-based routing tot geavanceerde patronen zoals protected routes met Stack.Protected, modale schermen, type-veilige navigatie en universele deep linking. Of je nu overstapt van React Navigation of helemaal opnieuw begint, na dit artikel kun je een professionele navigatiestructuur opzetten.
Wat is Expo Router en waarom v7?
Expo Router is een file-based router voor React Native en webapplicaties, gebouwd bovenop React Navigation. Het kernidee is eigenlijk vrij simpel: elk bestand in je app/-map wordt automatisch een route. De mappenstructuur weerspiegelt de URL-structuur, zowel op het web als in native apps.
Met v7, uitgebracht als onderdeel van Expo SDK 55, zijn er een hoop verbeteringen doorgevoerd. Eerlijk gezegd zijn sommige best ingrijpend:
Legacy Architecture is verdwenen — Expo Router v7 draait exclusief op de Nieuwe Architectuur met React Native 0.83 en React 19.2. Geen weg terug meer.
Stack.Protected en Tabs.Protected — een declaratieve API voor authenticatie en toegangscontrole, rechtstreeks in je layouts. Dit is persoonlijk mijn favoriete toevoeging.
Apple Zoom Transition — native shared element transitions zijn standaard ingeschakeld op iOS
Stack.Toolbar API — native UIToolbar voor acties en menu's onderaan het scherm (iOS)
SplitView ondersteuning — experimentele multi-pane interfaces voor tablets en desktops
Edge-to-edge is verplicht — voor Android 16+ is edgeToEdgeEnabled verwijderd uit app.json
resetOnFocus — de reset-prop in headless tabs is hernoemd voor duidelijkheid
Aan de slag: project opzetten
Goed, laten we beginnen met het concrete werk. Een nieuw Expo-project aanmaken met Router v7 kost je letterlijk één commando:
Dit genereert een project met de nieuwe standaardtemplate die native tabs en de /src/app-structuur bevat. Expo Router is standaard geconfigureerd, dus je hoeft niks extra te installeren.
Wil je Expo Router handmatig toevoegen aan een bestaand project? Dan heb je deze packages nodig:
Het kernprincipe van Expo Router is dat je mappenstructuur je navigatie definieert. Elk bestand in de app/-map wordt automatisch een route. Klinkt abstract, dus laten we het concreet maken.
Elke index.tsx matched de bovenliggende map. Het bestand [id].tsx met vierkante haken is een dynamische route — de parameter id wordt automatisch beschikbaar via useLocalSearchParams(). Best handig, toch?
Bestandsnotatie overzicht
Bestand
Betekenis
Voorbeeld URL
index.tsx
Standaardroute van een map
/
[param].tsx
Dynamische route
/product/42
[...rest].tsx
Catch-all route
/docs/a/b/c
(groep)/
Route groep (geen URL-effect)
Niet zichtbaar in URL
_layout.tsx
Layout definitie
Geen eigen route
+not-found.tsx
404-handler
Ongeldige URL's
Layouts begrijpen: _layout.tsx
Layouts zijn eerlijk gezegd het geheim van een goed gestructureerde Expo Router-app. Het _layout.tsx-bestand bepaalt hoe routes binnen een map zich tot elkaar verhouden — als stack, tabs, drawer of een volledig eigen opzet.
Root layout
De root layout (app/_layout.tsx) is het startpunt van je hele app. Hier laad je fonts, configureer je providers en beheer je het splash screen:
import { Stack } from 'expo-router';
import { useFonts } from 'expo-font';
import * as SplashScreen from 'expo-splash-screen';
import { useEffect } from 'react';
import { AuthProvider } from '@/context/AuthContext';
SplashScreen.preventAutoHideAsync();
export default function RootLayout() {
const [fontsLoaded] = useFonts({
'Inter-Regular': require('@/assets/fonts/Inter-Regular.ttf'),
'Inter-Bold': require('@/assets/fonts/Inter-Bold.ttf'),
});
useEffect(() => {
if (fontsLoaded) {
SplashScreen.hideAsync();
}
}, [fontsLoaded]);
if (!fontsLoaded) return null;
return (
);
}
Tab layout
Tabs zijn de hoofdhoofdstukken van je app — de plekken waar gebruikers het meest naartoe navigeren. Maak een (tabs)/_layout.tsx aan:
Expo Router biedt twee manieren om te navigeren: declaratief met het <Link>-component en imperatief met de useRouter()-hook. Beide hebben hun plek, afhankelijk van de situatie.
Declaratief met Link
Het <Link>-component werkt vergelijkbaar met een hyperlink op het web. Als je ervaring hebt met Next.js, voelt dit meteen vertrouwd:
import { Link } from 'expo-router';
import { View, Text, StyleSheet } from 'react-native';
export default function HomeScreen() {
return (
Welkom bij Mijn App
{/* Navigeer naar een vaste route */}
Over ons
{/* Navigeer naar een dynamische route */}
Bekijk profiel
{/* Navigeer met een object */}
Product bekijken
);
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', padding: 20 },
titel: { fontSize: 24, fontWeight: 'bold', marginBottom: 20 },
link: { fontSize: 16, color: '#007AFF', marginVertical: 8 },
});
Imperatief met useRouter
Voor programmatische navigatie — bijvoorbeeld na een formulierverzending of een API-aanroep — gebruik je de useRouter()-hook. Dit is waar het echt flexibel wordt:
import { useRouter } from 'expo-router';
import { Button, View } from 'react-native';
export default function BestelScherm() {
const router = useRouter();
const handleBestelling = async () => {
try {
const resultaat = await plaatsBestelling();
// navigate: gaat naar route of ontrolt bestaande stack
router.navigate(`/bestelling/${resultaat.id}`);
// push: voegt altijd een nieuw scherm toe aan de stack
// router.push(`/bestelling/${resultaat.id}`);
// replace: vervangt het huidige scherm (geen terug-knop)
// router.replace(`/bestelling/${resultaat.id}`);
} catch (error) {
router.navigate('/foutmelding');
}
};
return (
);
}
Verschil tussen navigate, push en replace
Dit is een onderdeel waar veel developers in het begin mee worstelen. Hier een kort overzicht:
Methode
Gedrag
Wanneer gebruiken
router.navigate()
Gaat naar route of ontrolt naar bestaande
Standaard voor de meeste navigatie
router.push()
Voegt altijd een nieuw scherm toe
Als je meerdere instanties wilt toestaan
router.replace()
Vervangt huidig scherm in de stack
Na login/redirect, geen terugknop gewenst
router.back()
Gaat terug naar vorig scherm
Terugnavigatie
router.dismiss()
Sluit het huidige modale scherm
Modals sluiten
Mijn tip: begin altijd met navigate(). Stap pas over op push() of replace() als je een concrete reden hebt.
Dynamische routes en parameters
Dynamische routes zijn essentieel voor content-gedreven apps. Een bestand met vierkante haken in de naam — zoals [id].tsx — vangt de waarde in de URL op als parameter. Denk aan productpagina's, gebruikersprofielen, dat soort dingen.
Let op dat useLocalSearchParams() getypeerd kan worden met een generiek type. Dat zorgt voor type-veiligheid bij het uitlezen van parameters — en je editor geeft je meteen autocompletion.
Protected routes: authenticatie met Stack.Protected
Oké, dit is waar het echt interessant wordt. Een van de grootste vernieuwingen in Expo Router v7 is de declaratieve Protected-API. Voorheen moest je met redirects en conditionals werken om routes te beschermen (wat al snel een rommeltje werd). Nu wikkel je simpelweg schermen in Stack.Protected of Tabs.Protected met een guard-prop.
Basisopzet met Stack.Protected
// app/_layout.tsx
import { Stack } from 'expo-router';
import { useAuth } from '@/context/AuthContext';
export default function RootLayout() {
const { isIngelogd } = useAuth();
return (
{/* Beschermde routes: alleen toegankelijk als ingelogd */}
{/* Publieke routes: alleen toegankelijk als NIET ingelogd */}
);
}
Zo werkt dit in de praktijk:
Als isIngelogdfalse is, zijn de (tabs) en modal routes gewoon onbereikbaar. De router opent automatisch het eerste beschikbare scherm — in dit geval inloggen.
Zodra de gebruiker inlogt en isIngelogd naar true flipt, wordt de layout opnieuw gerenderd en navigeert de app vanzelf naar (tabs). Geen handmatige redirect nodig.
Alle navigatiegeschiedenis van beschermde schermen wordt automatisch opgeschoond wanneer de guard-conditie verandert. Dat voorkomt dat gebruikers met de terug-knop weer op een beschermd scherm belanden.
Tabs.Protected voor rolgebaseerde toegang
Je kunt ook tabs voorwaardelijk tonen op basis van gebruikersrollen. Handig als je bijvoorbeeld een admin-paneel hebt dat niet voor iedereen zichtbaar moet zijn:
// app/(tabs)/_layout.tsx
import { Tabs } from 'expo-router';
import { useAuth } from '@/context/AuthContext';
import { Ionicons } from '@expo/vector-icons';
export default function TabLayout() {
const { gebruiker } = useAuth();
const isAdmin = gebruiker?.rol === 'admin';
return (
(
),
}}
/>
(
),
}}
/>
{/* Admin-tab alleen zichtbaar voor admins */}
(
),
}}
/>
);
}
De AuthContext implementeren
De Protected-API werkt het best in combinatie met een React Context die je authenticatiestatus beheert. Hier is een volledige implementatie die je als startpunt kunt gebruiken:
Modals zijn schermen die over je bestaande content heen zweven — ideaal voor formulieren, bevestigingen of snelle acties. In Expo Router configureer je een modal gewoon in de layout, net als elk ander scherm:
// app/_layout.tsx
import { Stack } from 'expo-router';
export default function RootLayout() {
return (
);
}
Navigeer naar een modal zoals je naar elke andere route zou gaan:
import { useRouter } from 'expo-router';
export default function ProductenScherm() {
const router = useRouter();
return (
);
}
Sluit een modal met router.dismiss() of router.back(). Het verschil is subtiel: dismiss() is specifiek bedoeld voor modals, terwijl back() altijd naar het vorige scherm gaat — ongeacht het type scherm.
Universele deep linking
Een van de krachtigste features van Expo Router (en eerlijk gezegd een van de redenen waarom ik het zo graag gebruik) is dat deep linking gewoon automatisch werkt. Elke route in je app is standaard bereikbaar via een URL — zowel intern als extern. Er is geen aparte linking.ts-configuratie meer nodig. Dat scheelt een hoop gedoe.
URI-scheme instellen
Definieer je app-scheme in app.json:
{
"expo": {
"scheme": "mijn-app"
}
}
Nu kun je vanuit een andere app navigeren met mijn-app://product/42 en het juiste scherm wordt geopend. Simpel.
Universal Links configureren (productie)
Custom URL-schemes hebben wel hun beperkingen: ze werken alleen als de app al geïnstalleerd is. Voor productie-apps wil je daarom Universal Links (iOS) en App Links (Android) instellen. Deze gebruiken standaard https://-URL's die de app openen als die geïnstalleerd is, of doorverwijzen naar een website als dat niet het geval is.
Een belangrijk voordeel van de nieuwe Protected-API: deep links worden ook netjes gecontroleerd. Als een niet-ingelogde gebruiker direct naar mijn-app://bestellingen/123 navigeert, wordt die automatisch omgeleid naar het inlogscherm. Na succesvolle login kan de app de gebruiker alsnog naar de oorspronkelijke bestemming brengen. Dat is een stuk eleganter dan de workarounds die je voorheen moest bouwen.
Type-veilige navigatie met Typed Routes
Expo Router ondersteunt typed routes: automatisch gegenereerde TypeScript-types op basis van je bestandsstructuur. Dit voorkomt typo's in route-paden en geeft je autocompletion in je editor. Een van die features die je pas echt gaat waarderen als je hem eenmaal hebt gebruikt.
Na het starten van de dev-server genereert Expo automatisch type-definities. Je krijgt nu gewoon TypeScript-fouten als je naar een route navigeert die niet bestaat:
import { Link } from 'expo-router';
// ✅ Correct — route bestaat
Profiel
// ❌ TypeScript-fout — route bestaat niet
Oeps
// ✅ Dynamische route met juiste parameters
Product
Veelgemaakte fouten en oplossingen
Tijdens het werken met Expo Router loop je onvermijdelijk tegen bepaalde fouten aan. Ik heb ze zelf ook allemaal gemaakt, dus hier zijn de meest voorkomende met directe oplossingen.
1. Null teruggeven vanuit de root component
Het is verleidelijk om null terug te geven terwijl fonts laden. Doe dit niet — het werkt niet goed met Expo Router en kan crashes veroorzaken. Gebruik in plaats daarvan het splash screen:
// ❌ Niet doen
if (!fontsLoaded) return null;
// ✅ Wel doen
import * as SplashScreen from 'expo-splash-screen';
SplashScreen.preventAutoHideAsync();
// Verberg het splash screen wanneer fonts geladen zijn
useEffect(() => {
if (fontsLoaded) SplashScreen.hideAsync();
}, [fontsLoaded]);
2. Niet-serialiseerbare parameters
Route-parameters ondersteunen alleen simpele types: string, number en boolean. Probeer geen objecten of arrays door te geven als parameters — dat gaat fout. Gebruik in plaats daarvan een ID en haal de data op in het doelscherm.
3. Dubbele schermdeclaraties
Een scherm mag maar één keer gedeclareerd worden in je navigatieboom. Als je een scherm in meerdere groepen nodig hebt, gebruik dan conditionals of protected routes in plaats van het scherm te dupliceren.
4. Tabs resetten bij schermwisseling
Dit is er eentje die me persoonlijk veel tijd heeft gekost. Plaats gedeelde schermen buiten de tabs-groep om te voorkomen dat tabs resetten wanneer je naar een scherm navigeert dat niet bij de actieve tab hoort.
Compleet voorbeeld: app-structuur in de praktijk
Goed, laten we alles samenbrengen. Hier is een realistische app-structuur voor een e-commerce app:
De root layout brengt alles samen met protected routes:
// app/_layout.tsx
import { Stack } from 'expo-router';
import { useAuth, AuthProvider } from '@/context/AuthContext';
function AppNavigatie() {
const { isIngelogd, isLaden } = useAuth();
if (isLaden) return null; // Splash screen is nog actief
return (
);
}
export default function RootLayout() {
return (
);
}
Prestatie-tips voor Expo Router v7
Navigatie kan een knelpunt worden in grotere apps. Hier zijn een paar concrete optimalisaties die je meteen kunt toepassen:
Lazy loading — In productiemodus laadt Expo Router routes pas wanneer ze nodig zijn. In ontwikkelingsmodus kun je async routes gebruiken voor snellere opstarttijden.
Minimale re-renders — Gebruik useLocalSearchParams() in plaats van useGlobalSearchParams() tenzij je echt globale parameters nodig hebt. Lokale params veroorzaken aanzienlijk minder re-renders.
Native Stack — Expo Router gebruikt standaard de native stack navigator (@react-navigation/native-stack), wat betere transitieanimaties en lagere geheugenkosten oplevert dan een JavaScript-gebaseerde stack.
Hermes bytecode diffing — Met SDK 55 zijn OTA-updates tot 75% kleiner dankzij bytecode diffing. Dat is een flinke verbetering voor de update-ervaring van je gebruikers.
Veelgestelde vragen
Wat is het verschil tussen Expo Router en React Navigation?
Expo Router is gebouwd bovenop React Navigation. Het belangrijkste verschil zit in de aanpak: Expo Router gebruikt file-based routing (je mappenstructuur bepaalt de routes), terwijl React Navigation een code-based configuratie vereist. Expo Router voegt daar automatische deep linking, typed routes en de declaratieve Protected-API aan toe. Onder de motorkap gebruiken ze dezelfde navigatiecomponenten, dus qua prestaties ontlopen ze elkaar niet veel.
Hoe bescherm ik routes tegen ongeautoriseerde toegang in Expo Router?
Gebruik de Stack.Protected of Tabs.Protected component met een guard-prop. Wanneer guard={false}, is het scherm onbereikbaar en wordt de gebruiker automatisch doorgestuurd naar het eerste beschikbare scherm. Dit werkt ook voor deep links. Belangrijk om te onthouden: protected routes zijn client-side en geen vervanging voor server-side authenticatie.
Kan ik Expo Router gebruiken zonder Expo?
Nee, helaas niet. Expo Router is specifiek ontworpen voor het Expo-ecosysteem en vereist Expo CLI en Metro bundler. Heb je een bare React Native-project zonder Expo? Dan kun je overwegen om te migreren naar een Expo-managed of bare workflow met Expo-modules, of je kunt bij React Navigation blijven.
Hoe werkt deep linking met Expo Router?
Deep linking werkt automatisch — dat is een van de grote voordelen. Elke route is standaard bereikbaar via een URL. Voor ontwikkeling stel je een custom URI-scheme in via app.json. Voor productie configureer je Universal Links (iOS) en App Links (Android) zodat https://-URL's direct je app openen. Geen aparte linking.ts-configuratie meer nodig.
Hoe migreer ik van React Navigation naar Expo Router?
Begin met het opsplitsen van je schermcomponenten in aparte bestanden. Maak vervolgens een app/-map aan die je navigatiestructuur weerspiegelt. Vervang je stack- en tab-configuraties door _layout.tsx-bestanden. Expo biedt een officiële migratiegids op docs.expo.dev/router/migrate/from-react-navigation. Mijn belangrijkste advies: migreer stapsgewijs, scherm voor scherm. Probeer niet alles in één keer om te gooien.
Marcus is a senior React Native engineer based in Berlin with twelve years in mobile, the last seven of them in JavaScript-driven cross-platform work. He spent three years at SoundCloud rewriting the listener app's playback queue and offline-download stack on top of TrackPlayer, and another two and a half years at Delivery Hero leading the rider app's reliability work across 30+ markets.
He started out as a native iOS developer at a smaller agency shipping white-label banking apps, which still shows up in his writing whenever the topic turns to bridging Swift code or wrangling Xcode build settings. Most of his posts here cover Reanimated 3 and Gesture Handler internals, CodePush versus EAS Update tradeoffs, and the kind of Hermes crash reports that only show up in production. He occasionally speaks at React Native EU.
Volledige gids voor Expo Push Notifications in 2026: EAS-credentials voor APNs en FCM v1, hooks voor handlers, server-side verzending met expo-server-sdk en debugging-tips uit de praktijk.
Een compleet overzicht van Expo SDK 55 met React Native 0.83, React 19.2, Hermes v1, het definitieve einde van de Legacy Architecture, nieuwe navigatie-API's en een stapsgewijze migratiehandleiding.