React
Route groups e layout annidati nell'App Router
Le cartelle tra parentesi non aggiungono segmenti all'URL: separano layout, e i layout annidati restano montati mentre navighi. Regole e trappole.
In sintesi
- Una cartella tra parentesi è un route group: non compare nell'URL.
- Ogni gruppo può avere il proprio layout radice, con html e body.
- Un layout annidato non si ri-renderizza se navighi sotto di lui.
- Due page che risolvono lo stesso URL fermano la build.
Un route group è una cartella tra parentesi che non aggiunge un segmento all'URL: serve a dare a due parti del sito layout e confini diversi. Il layout annidato, invece, resta montato quando navighi e non ri-renderizza.
La struttura
app/
layout.tsx # root: html, body, font
(marketing)/
layout.tsx # public header
page.tsx # /
pricing/page.tsx # /pricing
(app)/
layout.tsx # shell with sidebar
dashboard/
layout.tsx # section title and tabs
page.tsx # /dashboard
settings/page.tsx # /dashboard/settings
(auth)/
layout.tsx
login/page.tsx # /login
Nessuna delle tre cartelle tra parentesi compare negli URL: /pricing, /dashboard/settings e /login sono gli indirizzi reali. Il gruppo decide solo quale layout avvolge la pagina.
Cosa fa un layout annidato
Un layout riceve children e resta montato finché la navigazione resta dentro il suo segmento. Passare da /dashboard a /dashboard/settings ri-renderizza la pagina, non la shell: lo stato della sidebar sopravvive.
// app/(app)/dashboard/layout.tsx
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
return (
Dashboard
{children}
);
}
Il layout non riceve searchParams: quelli arrivano solo alla pagina. Se un filtro vive nell'URL e serve anche alla shell, leggi il parametro nella pagina e passalo come prop a un componente client.
Quando il gruppo serve
| Caso | Gruppi | Perché |
|---|---|---|
| Sito marketing e prodotto | (marketing) e (app) | Header e navigazione diversi, un solo dominio |
| Due layout radice | (shop) e (docs) | Ognuno con html e body propri; il passaggio tra i due ricarica la pagina |
| Login e reset password | (auth) | Fuori dalla shell, senza sidebar |
Gli errori che si pagano
- Due
page.tsxche risolvono lo stesso URL: la build si ferma con un conflitto di path. - Un layout che legge
searchParams: non li riceve. - Logica di sessione nel layout radice: costa una query a ogni richiesta, anche per le pagine che non la usano.
- Gruppi creati per raggruppare file: se i layout sono identici, il gruppo non fa nulla.
Per montare i layout servono blocchi, non pagine intere: header, sidebar e footer entrano nel tuo repository e seguono il tuo albero.
La struttura generale è in struttura di un progetto App Router; per i pannelli paralleli c'è route parallele e per l'attesa streaming e Suspense. Il confronto con un altro framework è in Astro vs Next.js.
npx next build
Limite noto: un route group non cambia l'URL né il percorso del file. Se due gruppi dichiarano la stessa rotta, Next.js non sceglie: ferma la build.
Domande frequenti
Un route group cambia l'URL?
No. Le parentesi tolgono la cartella dal percorso: pricing dentro (marketing) resta /pricing.
Posso avere due layout radice?
Sì, se ogni gruppo ha il proprio layout con html e body. Il passaggio da un gruppo all'altro ricarica la pagina per intero, quindi perdi la navigazione client.
Il layout si ri-renderizza a ogni navigazione?
No, finché resti dentro il suo segmento. Cambia quando la navigazione esce dal layout o quando il dato condiviso viene invalidato sul server.
Dove metto il controllo di sessione?
Nella pagina o in un componente server chiamato dalla pagina. Nel layout costa una query anche per le rotte che non lo usano.
Le route group funzionano con le parallel routes?
Sì, uno slot può stare dentro un gruppo. Il gruppo decide il layout, lo slot decide il pannello: sono due assi diversi.