Expo Router in React Native: Guida Completa alla Navigazione File-Based
Expo Router per React Native: routing basato su file, layout, route dinamiche, navigazione tipizzata, tab, modali, middleware e migrazione SDK 56.

Expo Router porta il routing basato su file in React Native, sostituendo la configurazione manuale della navigazione con un approccio basato su convenzioni ispirato a Next.js. A partire da Expo SDK 57 e Expo Router, la costruzione della navigazione multipiattaforma per Android, iOS e web richiede semplicemente la creazione di file nella directory corretta.
I nuovi progetti Expo includono Expo Router preconfigurato. Il comando npx create-expo-app@latest --template default genera un progetto con routing basato su file pronto all'uso. Per i progetti esistenti, è sufficiente installare expo-router e aggiornare il punto di ingresso.
Come funziona il routing basato su file in Expo Router
Ogni file nella directory app diventa automaticamente una route. Il percorso del file corrisponde direttamente al percorso URL, eliminando la necessità di una configurazione di navigazione centralizzata. Un file in app/settings.tsx crea una route /settings, mentre app/profile/edit.tsx corrisponde a /profile/edit.
Questo approccio offre tre vantaggi chiave rispetto alla configurazione tradizionale di React Navigation:
- Zero configurazione: le route esistono nel momento in cui viene creato un file
- Deep linking automatico: ogni schermata ha un URL, consentendo condivisione e testing
- Navigazione tipizzata: TypeScript conosce le route esistenti in fase di compilazione
import { View, Text, StyleSheet } from 'react-native'
import { Link } from 'expo-router'
export default function HomeScreen() {
return (
<View style={styles.container}>
<Text style={styles.title}>Welcome</Text>
{/* Link maps directly to file path */}
<Link href="/settings" style={styles.link}>
Open Settings
</Link>
<Link href="/profile/edit" style={styles.link}>
Edit Profile
</Link>
</View>
)
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', padding: 24 },
title: { fontSize: 28, fontWeight: 'bold', marginBottom: 16 },
link: { fontSize: 16, color: '#61DAFB', marginTop: 12 },
})Il componente Link gestisce la navigazione su tutte le piattaforme. Sul web, renderizza un tag anchor con attributi href corretti per la SEO. Sulle piattaforme native, attiva una navigazione basata su stack.
Struttura del progetto e file di layout
Expo Router utilizza file _layout.tsx per definire i container di navigazione. Ogni directory può avere il proprio layout, creando gerarchie di navigazione annidate. Il layout root avvolge l'intera app, mentre i layout annidati controllano sezioni specifiche.
Una struttura di progetto tipica appare così:
app/
_layout.tsx # Root layout (Stack or custom)
index.tsx # Home screen (/)
(tabs)/ # Tab group (parentheses = route group)
_layout.tsx # Tab navigator
home.tsx # /home tab
search.tsx # /search tab
profile.tsx # /profile tab
settings/
_layout.tsx # Settings stack layout
index.tsx # /settings
notifications.tsx # /settings/notifications
privacy.tsx # /settings/privacyI gruppi di route, directory racchiuse tra parentesi, organizzano i file senza influenzare l'URL. La directory (tabs) sopra crea un navigatore a tab, ma gli URL rimangono /home, /search e /profile invece di /tabs/home.
import { Stack } from 'expo-router'
export default function RootLayout() {
return (
<Stack
screenOptions={{
headerStyle: { backgroundColor: '#1a1a2e' },
headerTintColor: '#ffffff',
headerTitleStyle: { fontWeight: '600' },
}}
>
<Stack.Screen name="index" options={{ title: 'Home' }} />
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="settings" options={{ title: 'Settings' }} />
</Stack>
)
}Il layout root serve anche come punto di caricamento per i font, inizializzazione dei provider e configurazione delle impostazioni globali.
Costruzione della navigazione a tab con Expo Router
La navigazione a tab richiede un file _layout.tsx all'interno di un gruppo di route. Expo Router fornisce sia NativeTabs per esperienze tab specifiche per piattaforma sia il componente standard Tabs per la maggior parte dei casi d'uso. Da SDK 56, NativeTabs supporta la proprietà disabled per impedire la selezione dei tab mantenendoli visibili.
import { Tabs } from 'expo-router'
import { Ionicons } from '@expo/vector-icons'
export default function TabLayout() {
return (
<Tabs
screenOptions={{
tabBarActiveTintColor: '#61DAFB',
tabBarInactiveTintColor: '#888',
tabBarStyle: {
backgroundColor: '#1a1a2e',
borderTopColor: '#2d2d44',
},
}}
>
<Tabs.Screen
name="home"
options={{
title: 'Home',
tabBarIcon: ({ color, size }) => (
<Ionicons name="home" size={size} color={color} />
),
}}
/>
<Tabs.Screen
name="search"
options={{
title: 'Search',
tabBarIcon: ({ color, size }) => (
<Ionicons name="search" size={size} color={color} />
),
}}
/>
<Tabs.Screen
name="profile"
options={{
title: 'Profile',
tabBarIcon: ({ color, size }) => (
<Ionicons name="person" size={size} color={color} />
),
}}
/>
</Tabs>
)
}Ogni file di schermata tab esporta un componente React standard. L'icona della tab bar, l'etichetta e il badge vengono configurati attraverso la prop options nel layout.
Route dinamiche e parametri di route
I segmenti dinamici utilizzano parentesi quadre nel nome del file. Un file chiamato [id].tsx cattura un singolo segmento, mentre [...slug].tsx cattura tutti i segmenti rimanenti.
import { View, Text, StyleSheet } from 'react-native'
import { useLocalSearchParams, Stack } from 'expo-router'
export default function ProductScreen() {
// Extract the dynamic parameter from the URL
const { id } = useLocalSearchParams<{ id: string }>()
return (
<View style={styles.container}>
<Stack.Screen options={{ title: \`Product \${id}\` }} />
<Text style={styles.heading}>Product Details</Text>
<Text style={styles.id}>ID: {id}</Text>
</View>
)
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 24 },
heading: { fontSize: 24, fontWeight: 'bold', marginBottom: 8 },
id: { fontSize: 16, color: '#888' },
})Navigando verso /product/42 si renderizza questa schermata con id impostato a "42". L'hook useLocalSearchParams fornisce accesso tipizzato a tutti i parametri di route.
Per le route catch-all, [...slug].tsx cattura interi segmenti di percorso:
import { useLocalSearchParams } from 'expo-router'
export default function DocsScreen() {
// /docs/getting-started/installation → slug = ['getting-started', 'installation']
const { slug } = useLocalSearchParams<{ slug: string[] }>()
return <DocViewer path={slug.join('/')} />
}Pronto a superare i tuoi colloqui su React Native?
Pratica con i nostri simulatori interattivi, flashcards e test tecnici.
Route tipizzate per la sicurezza in fase di compilazione
Expo Router genera automaticamente i tipi delle route quando typed routes è abilitato. Questo rileva i link non validi in fase di compilazione invece che a runtime.
Abilitare le route tipizzate in app.json:
{
"expo": {
"experiments": {
"typedRoutes": true
}
}
}Una volta abilitato, la prop href su Link e l'argomento di router.push() accettano solo stringhe di route valide:
import { router } from 'expo-router'
function handleCheckout(cartId: string) {
// TypeScript validates this route exists
router.push(\`/product/\${cartId}\`)
// This would cause a compile error if /nonexistent doesn't exist
// router.push('/nonexistent')
}Le route tipizzate si integrano bene con useLocalSearchParams. I tipi generati assicurano che i nomi dei parametri corrispondano tra la definizione della route e il componente che la consuma, prevenendo bug sottili che emergerebbero solo navigando verso una schermata specifica.
Schermate modali e opzioni di presentazione
Le modali in Expo Router sono schermate normali configurate con presentation: 'modal' nel layout. Questo approccio mantiene intatta la convenzione basata su file: una modale è semplicemente un'altra route.
import { Stack } from 'expo-router'
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="index" />
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
{/* Modal screen slides up from the bottom */}
<Stack.Screen
name="create-post"
options={{
presentation: 'modal',
headerTitle: 'New Post',
}}
/>
</Stack>
)
}import { View, TextInput, Button, StyleSheet } from 'react-native'
import { router } from 'expo-router'
import { useState } from 'react'
export default function CreatePostModal() {
const [title, setTitle] = useState('')
const handleSubmit = () => {
// Submit logic here
router.back() // Dismiss the modal
}
return (
<View style={styles.container}>
<TextInput
style={styles.input}
placeholder="Post title"
value={title}
onChangeText={setTitle}
/>
<Button title="Publish" onPress={handleSubmit} />
</View>
)
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 24 },
input: {
borderWidth: 1,
borderColor: '#333',
borderRadius: 8,
padding: 12,
fontSize: 16,
marginBottom: 16,
},
})Navigando verso /create-post si attiva la presentazione modale. La chiamata a router.back() chiude la modale, tornando alla schermata precedente nello stack.
Navigazione programmatica e API del Router
Oltre al componente Link, Expo Router fornisce un'API imperativa attraverso l'oggetto router. Questa gestisce la navigazione attivata dalla logica di business piuttosto che dalle interazioni dell'utente.
import { router } from 'expo-router'
// Push a new screen onto the stack
router.push('/profile/settings')
// Replace the current screen (no back button)
router.replace('/login')
// Go back to the previous screen
router.back()
// Navigate with parameters
router.push({
pathname: '/product/[id]',
params: { id: '42', source: 'recommendations' },
})
// Check if going back is possible
import { useRouter } from 'expo-router'
function BackButton() {
const router = useRouter()
return router.canGoBack() ? (
<Button title="Back" onPress={() => router.back()} />
) : null
}La distinzione tra router.push e router.replace è importante per i flussi di autenticazione in React Native. Dopo un login riuscito, router.replace('/dashboard') impedisce all'utente di tornare alla schermata di login.
router.replace() sostituisce l'entry corrente nella cronologia di navigazione. Per i guard di autenticazione che reindirizzano utenti non autenticati, utilizzare <Redirect href="/login" /> all'interno del render del componente: si attiva durante la fase di render e funziona correttamente con il rendering lato server sul web.
Middleware e protezione delle route
Expo Router fornisce middleware server per la logica a livello di route. Il file +middleware.ts intercetta le richieste prima che raggiungano il componente della route, abilitando controlli di autenticazione, redirect e manipolazione degli header.
import { type MiddlewareRequest } from 'expo-router/server'
export function middleware(request: MiddlewareRequest) {
const { pathname } = request.nextUrl
// Protect dashboard routes
const protectedPaths = ['/dashboard', '/settings', '/profile']
const isProtected = protectedPaths.some(p => pathname.startsWith(p))
if (isProtected) {
const token = request.cookies.get('session')
if (!token) {
return Response.redirect(new URL('/login', request.url))
}
}
return undefined // Continue to route
}Sulle piattaforme native dove il middleware server non viene eseguito, la protezione delle route si basa su guard lato client. Un pattern comune avvolge i layout protetti con un controllo di autenticazione:
import { Redirect, Stack } from 'expo-router'
import { useAuth } from '@/hooks/useAuth'
export default function AuthenticatedLayout() {
const { isLoggedIn, isLoading } = useAuth()
if (isLoading) return null
if (!isLoggedIn) return <Redirect href="/login" />
return <Stack />
}Il middleware server funziona solo sul web con rendering lato server. Per le app native, implementare sempre guard di route lato client nei componenti di layout. Combinare entrambi gli approcci garantisce una protezione coerente su tutte le piattaforme.
Migrazione SDK 56: Fork di React Navigation
A partire da Expo SDK 56, Expo Router non dipende più dai pacchetti @react-navigation/*. Se il progetto importa direttamente da React Navigation, aggiornare gli import:
| Pacchetto originale | Nuova posizione |
|---|---|
@react-navigation/native | expo-router/react-navigation |
@react-navigation/stack | expo-router/js-stack |
@react-navigation/bottom-tabs | expo-router/js-tabs |
Eseguire il codemod per aggiornare automaticamente gli import:
npx expo-codemod sdk-56-expo-router-react-navigation-replace srcL'API a runtime rimane invariata. Solo gli specificatori di modulo sono stati spostati. Le librerie di terze parti che ancora importano da @react-navigation/core funzionano automaticamente perché Expo CLI riscrive quegli import durante il bundling.
Fonti
- Documentazione Expo Router - guida ufficiale e riferimento API
- Changelog Expo SDK 57 - note di rilascio della versione stabile attuale
- Post sul blog Expo Router v56 - spiegazione del fork di React Navigation
- Guida alla migrazione da SDK 55 a 56 - passaggi per la migrazione degli import
Cosa si aspettano gli intervistatori su Expo Router
- Expo Router mappa automaticamente ogni file nella directory
appa una route, eliminando la configurazione manuale della navigazione - I layout definiti tramite file
_layout.tsxcreano gerarchie di navigazione per stack, tab e drawer - Le route dinamiche con
[param].tsxe le route catch-all con[...slug].tsxgestiscono la navigazione parametrizzata con supporto TypeScript completo - Le route tipizzate rilevano link di navigazione non validi in fase di compilazione quando abilitate in
app.json - Schermate modali, navigazione programmatica via
router.push/replace/backe middleware server completano il toolkit di routing - I gruppi di route tramite directory con parentesi organizzano il codice senza influenzare gli URL
- I guard di route lato client nei layout gestiscono l'autenticazione sul nativo, mentre il middleware server copre il web
- Da SDK 56, gli import da
@react-navigation/*devono essere aggiornati agli equivalentiexpo-router/*
Per approfondire l'architettura React Native e la preparazione ai colloqui, consultare il modulo sui native modules.
Inizia a praticare!
Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.
Sapresti trovare il bug in React Native?
Uno snippet reale, un bug nascosto, un tentativo al giorno. Senza account per provare.

Scritto da
Anthony Fillion-MailletFondatore di SharpSkill
Sviluppatore fullstack da oltre 10 anni. Guida SharpSkill e risponde di tutto ciò che vi viene pubblicato.
Aggiornato il 6 ottobre 2026
Tag
Condividi
Articoli correlati

React Native 0.87 e SwiftPM nel 2026: Build iOS Moderno e Domande per Colloqui
React Native 0.87 introduce il supporto sperimentale per Swift Package Manager, eliminando CocoaPods dalle build iOS. Questa guida copre la configurazione di SwiftPM, la Strict TypeScript API, le ottimizzazioni di Metro 0.87 e domande per colloqui di sviluppatori mobile senior.

Hermes V1 in React Native 0.84: Performance, Bytecode Precompilato e Domande per Colloqui Tecnici
Un'analisi approfondita di Hermes V1 come motore JavaScript predefinito in React Native 0.84. Questo articolo copre la precompilazione del bytecode, il garbage collector concorrente Hades, le strategie di ottimizzazione della memoria e le domande tecniche per colloqui.

React Native 0.86 nel 2026: Edge-to-Edge Android, DevTools e Domande per Colloqui
Padroneggia React Native 0.86 con il supporto edge-to-edge per Android 15+, miglioramenti DevTools e domande pratiche per colloqui. Guida completa con esempi di codice.