Qual è la struttura standard delle directory di un progetto Next.js?
Una struttura standard delle directory di un progetto Next.js non è un unico albero di cartelle corretto che si applichi in modo identico a ogni team. Significa invece seguire convenzioni definite per i file di routing che il framework deve interpretare, collocando il resto del codice in base alla natura del progetto. Per un nuovo progetto, un approccio facile da comprendere consiste di solito nel rendere src/app il centro per URL e punti di ingresso delle pagine, separare in components l'interfaccia utente usata in più punti, collocare in features la logica specifica delle funzionalità quando necessario e mantenere le utility condivise in lib. App Router è l'approccio di routing principale nell'attuale Next.js, mentre Pages Router continua a essere supportato. nextjs.orgnextjs.org
Quando si definisce una struttura per la prima volta, l'importante non è creare quante più cartelle possibile. Occorre prima distinguere quali file creano URL, quali gestiscono layout, schermate di errore e API e quale codice viene usato soltanto all'interno di una determinata schermata. Questo articolo esamina una struttura manutenibile incentrata su App Router, basata su tali distinzioni.
Perché non esiste una “struttura standard unica” in Next.js?
Next.js fornisce convenzioni per creare route da file e cartelle, ma non prescrive con precisione quali cartelle debbano contenere ogni componente e ogni elemento di logica di business. In particolare, in App Router le cartelle rappresentano segmenti URL e file speciali come page.tsx e route.ts creano veri punti di ingresso pubblici. Al contrario, i file ordinari collocati all'interno di una route non diventano route esterne per impostazione predefinita. nextjs.org
A causa di questa caratteristica, le strutture possono differire anche tra applicazioni Next.js. Un piccolo sito di marketing potrebbe richiedere soltanto app e alcuni componenti condivisi. Invece, in un servizio con più domini, come dashboard, ordini e account, separare il codice specifico delle funzionalità dal codice condiviso rende più facile comprendere l'ambito delle modifiche. Non si tratta di una regola assoluta richiesta da Next.js, bensì di un approccio all'organizzazione del codice che un team sceglie di adottare sopra le convenzioni di routing.
Pertanto, è meno fonte di confusione pensare a “standard” nei due livelli seguenti.
| Categoria | Natura | Esempi tipici |
|---|---|---|
| Convenzioni interpretate da Next.js | I nomi e le posizioni dei file influenzano direttamente il comportamento | app/page.tsx, app/layout.tsx, app/api/users/route.ts |
| Convenzioni definite dal progetto | Il team definisce nomi e confini per i propri scopi | components, features, lib, hooks, types |
Modificare arbitrariamente il primo livello può alterare il routing o il comportamento speciale dell'interfaccia utente. Il secondo livello può essere omesso o combinato a seconda della scala e della complessità del dominio. Per esempio, è possibile una struttura senza features e non è necessario suddividere eccessivamente ogni parte di codice in un piccolo progetto.
Perché i nuovi progetti dovrebbero essere progettati attorno ad App Router?
App Router è un approccio per creare pagine e layout basato sulla directory app. La documentazione ufficiale raccomanda di passare da Pages Router ad App Router per sfruttare le funzionalità React più recenti, mentre Pages Router stesso resta supportato. Di conseguenza, è necessario comprendere le convenzioni di pages quando si esegue la manutenzione o si studia una struttura di progetto esistente, ma è naturale considerare App Router come candidato predefinito nella progettazione di una nuova struttura. nextjs.org
Il punto chiave di App Router è collegare la gerarchia degli URL alla gerarchia dei file. Per esempio, app/dashboard/page.tsx diventa il punto di ingresso della pagina /dashboard, mentre app/dashboard/layout.tsx, posto al suo interno, può fornire un layout applicato alle sottoroute della dashboard. Il file radice app/layout.tsx è il layout radice che avvolge tutte le route. nextjs.org
Questo approccio è particolarmente adatto a mantenere vicino il codice a livello di schermata. Schede, tabelle e filtri dell'interfaccia utente necessari soltanto a /dashboard possono risiedere sotto app/dashboard, mentre pulsanti o campi di input riutilizzati in più schermate possono essere spostati in una cartella condivisa esterna. Il criterio fondamentale non è “quale tecnologia è stata usata per scrivere questo file”, ma “in quale ambito viene riutilizzato?”.
Tuttavia, usare App Router non significa che tutto il codice debba stare in app. Puoi considerare app come un confine che rende semplici da leggere gli URL e i file speciali e separare, quando necessario, il codice condiviso o implementazioni complesse di funzionalità in altre cartelle. Questa separazione non viene eseguita automaticamente da Next.js: è una convenzione strutturale che il team definisce e applica in modo coerente.
Quando usare una cartella src e cosa resta nella directory radice?
src è facoltativa. Quando la usi, puoi raccogliere app e il codice sorgente dell'applicazione sotto src, separando visivamente i file di configurazione dal codice di runtime. Al contrario, public, package.json, next.config.js, tsconfig.json e i file .env.* appartengono alla directory radice del progetto. nextjs.org
Di seguito è riportato un esempio facile da comprendere quando si usa App Router insieme a src.
my-app/
├─ public/
│ ├─ images/
│ └─ fonts/
├─ src/
│ ├─ app/
│ │ ├─ layout.tsx
│ │ ├─ page.tsx
│ │ ├─ globals.css
│ │ ├─ (marketing)/
│ │ │ └─ about/
│ │ │ └─ page.tsx
│ │ ├─ dashboard/
│ │ │ ├─ layout.tsx
│ │ │ ├─ page.tsx
│ │ │ ├─ loading.tsx
│ │ │ ├─ error.tsx
│ │ │ └─ _components/
│ │ └─ api/
│ │ └─ users/
│ │ └─ route.ts
│ ├─ components/
│ ├─ features/
│ ├─ lib/
│ ├─ hooks/
│ └─ types/
├─ .env.local
├─ next.config.js
├─ package.json
└─ tsconfig.json
In questo esempio, src è soltanto un confine per il codice dell'applicazione, non un meccanismo obbligatorio che modifica il comportamento. Se un progetto esistente ha già app nella radice, seguire con coerenza la convenzione stabilita può essere preferibile rispetto a forzare l'aggiunta di src. In particolare, se esistono directory denominate app o pages sia nella radice sia in src, ha precedenza la directory radice; durante una migrazione è quindi importante non lasciare strutture duplicate per un periodo prolungato. nextjs.org
Il criterio pratico per scegliere src è semplice. È utile se vuoi separare chiaramente i file di configurazione dal codice del prodotto oppure prevedi che il numero di file sorgente crescerà. Al contrario, non è necessario adottarla in un progetto di apprendimento o in un progetto molto piccolo la cui struttura radice è già chiara.
Quali file nella cartella app creano route effettive?
In App Router, le cartelle rappresentano parti di un URL, ovvero segmenti. Tuttavia, creare soltanto una cartella app/dashboard non rende /dashboard una pagina pubblica. Se quella cartella contiene page.tsx, diventa una route che fornisce l'interfaccia utente di una pagina; se contiene route.ts, diventa un endpoint API basato su un Route Handler. nextjs.org
Per esempio, considera la struttura seguente.
src/app/
├─ page.tsx
├─ about/
│ └─ page.tsx
├─ dashboard/
│ ├─ page.tsx
│ └─ reports/
│ └─ page.tsx
└─ api/
└─ users/
└─ route.ts
In questo caso, page.tsx corrisponde rispettivamente a /, /about, /dashboard e /dashboard/reports. api/users/route.ts non è una pagina dell'interfaccia utente, ma definisce un endpoint API. Una volta compreso che page.tsx e route.ts fungono da punti di ingresso pubblici di una route, diventa chiaro perché altri file possano risiedere nella stessa cartella. nextjs.org
Questa proprietà può essere vista come colocation. La colocation è un approccio organizzativo che mantiene vicino il codice correlato. Per esempio, i componenti tabella usati soltanto in dashboard, le funzioni di formattazione specifiche della schermata e il codice di trasformazione dei dati di test possono essere collocati vicino ad app/dashboard. Ciò non significa che non si debbano mai usare cartelle condivise. È sufficiente spostare il codice riutilizzato da altre route in cartelle con un ambito più ampio.
Come separare i file layout, loading ed error?
layout.tsx gestisce la struttura condivisa dell'interfaccia utente. Il file radice app/layout.tsx avvolge tutte le route, mentre un layout.tsx in una cartella figlia viene applicato in modo annidato alle relative sottoroute. Per esempio, se la navigazione della dashboard è condivisa da /dashboard e /dashboard/reports, può risiedere in app/dashboard/layout.tsx. nextjs.org
page.tsx è l'interfaccia utente della pagina visualizzata in uno specifico percorso. Se un layout è la struttura esterna che si ripete, una pagina è più vicina al contenuto specifico del percorso che cambia al suo interno. Separarli significa non dover ripetere la navigazione o le cornici condivise in ogni pagina.
App Router fornisce inoltre file riservati per l'interfaccia utente di stati specifici. loading.tsx viene usato per l'interfaccia di caricamento, error.tsx per l'interfaccia di errore e not-found.tsx per l'interfaccia dei contenuti non trovati. A differenza dei normali file di componenti, Next.js interpreta questi file per ruoli specifici; vanno quindi collocati tenendo presenti ruolo e ambito. nextjs.org
Per esempio, se vuoi mostrare una schermata separata durante la preparazione dei dati sotto /dashboard, puoi prendere in considerazione app/dashboard/loading.tsx; se ti serve una schermata per la gestione degli errori in tale ambito, puoi considerare app/dashboard/error.tsx. Non occorre aggiungere questi file meccanicamente in ogni cartella. Decidere in base al fatto che ogni route necessiti davvero di un'interfaccia distinta per stati di attesa, errore o contenuto non trovato mantiene la struttura concisa.
Come sono rappresentate le route dinamiche e le route API nei nomi delle cartelle?
Usa la notazione tra parentesi quadre quando una parte di una route non è predeterminata. [slug] indica un segmento dinamico, [...slug] indica tutti i sottosegmenti successivi e [[...slug]] indica una forma facoltativa in cui tali sottosegmenti possono essere assenti. nextjs.org
Per esempio, una pagina il cui identificatore del post cambia può essere strutturata come segue.
src/app/posts/
└─ [slug]/
└─ page.tsx
In questa struttura, [slug] non è un nome di cartella fisso; è un segnaposto che riceve la parte variabile dell'URL. Al contrario, quando più livelli di una route devono essere gestiti da un'unica convenzione, considera [...slug] o [[...slug]]. La notazione da scegliere dipende dal fatto che debba esistere almeno un sottopercorso e che il caso senza percorso debba essere gestito dalla stessa schermata. nextjs.org
Quando crei API, route.ts è il punto di ingresso di un Route Handler. Pertanto, puoi rappresentare la struttura dell'URL con cartelle come app/api/users/route.ts e collocare route.ts alla fine. Sia per le pagine sia per le API, la lettura della gerarchia delle cartelle consente di dedurre il percorso approssimativo. Tuttavia, è preferibile separare in modo appropriato i file di implementazione dedicati, così che il codice dell'interfaccia utente e quello dell'elaborazione lato server non risultino eccessivamente mescolati e lunghi nella stessa area. nextjs.org
Perché sono necessari route group e cartelle private?
Una cartella racchiusa tra parentesi, come (marketing), è un route group. È un raggruppamento logico che non viene incluso nell'URL. Per esempio, puoi raggruppare pagine orientate al marketing, come le informazioni aziendali e i prezzi, gestendo al contempo le schermate di utilizzo del prodotto in una struttura separata. app/(marketing)/about/page.tsx diventa la route /about senza il nome del gruppo. nextjs.org
I route group sono utili quando vuoi mostrare confini di layout o proprietà del codice senza modificare l'URL. Tuttavia, poiché il nome del gruppo non compare nell'URL, si verifica un conflitto se gruppi separati creano infine lo stesso URL. Inoltre, una configurazione che si sposta tra più layout radice può causare un caricamento completo della pagina; pertanto, la suddivisione dei layout radice non dovrebbe essere applicata con leggerezza solo per rendere ordinate le cartelle. nextjs.org
Le cartelle che iniziano con un trattino basso, come _components e _lib, sono cartelle private e vengono escluse dal routing. In App Router, i file ordinari non diventano route per impostazione predefinita, quindi il trattino basso non è strettamente necessario. Può comunque essere utile quando vuoi contrassegnare visivamente il confine tra file speciali di routing e implementazione interna, oppure evitare confusione con nomi di file riservati. nextjs.org
Per esempio, app/dashboard/_components/summary-card.tsx comunica che si tratta di un'interfaccia utente specifica della dashboard. Tuttavia, se quel componente inizia a essere usato ripetutamente in altre funzionalità, è più naturale valutare di spostarlo dalla cartella con trattino basso in components condivisa o in un confine di funzionalità appropriato. Ricorda che un prefisso di cartella non è un meccanismo di controllo degli accessi: è una notazione che comunica il ruolo del codice.
Come distinguere components, features, lib, hooks e types?
Queste cartelle sono scelte organizzative facoltative, non convenzioni riservate di App Router. Pertanto, i confini di responsabilità concordati dal team contano più dei nomi stessi. Le seguenti distinzioni costituiscono un punto di partenza comune.
| Cartella | Codice normalmente collocato | Criterio di collocazione |
|---|---|---|
components | Interfaccia utente riutilizzata in più schermate | Non è legata a uno specifico URL o dominio? |
features | Implementazione a livello di funzionalità o dominio | Esiste un concetto di business chiaro, come account, ordini o dashboard? |
lib | Utility e client condivisi | È uno strumento condiviso anziché interfaccia utente? |
hooks | Hook riutilizzabili | Più componenti condividono lo stesso stato o comportamento? |
types | Tipi condivisi | Più aree fanno riferimento alle stesse definizioni di tipo? |
components può contenere non solo pulsanti generici, ma anche interfacce composte condivise tra più funzionalità. Tuttavia, rendere fin dall'inizio ogni elemento dell'interfaccia utente un componente condiviso globalmente può astrarre implementazioni che in realtà servono a una sola schermata. Un altro approccio consiste nel mantenerlo inizialmente vicino alla route, per poi spostarlo quando viene usato stabilmente in due o più punti e dispone di una chiara interfaccia condivisa.
features è particolarmente facile da leggere quando è necessaria una struttura centrata sul dominio. Per esempio, se ordini e account hanno ciascuno schermate, interfaccia utente e codice di elaborazione dati indipendenti, puoi raggrupparli come features/orders e features/account. Al contrario, creare troppe cartelle di funzionalità in un sito semplice può costringere le persone a passare attraverso molte cartelle solo per trovare i file. È meglio introdurle soltanto quando i confini delle funzionalità coincidono con concetti reali del prodotto.
lib è una posizione candidata per il codice di base non UI, come utility condivise o client server. Tuttavia, se ogni file di funzioni si accumula in lib, questa può diventare un'ampia area di archiviazione difficile da comprendere. Una regola pratica consiste nel mantenere gli strumenti usati da una sola funzionalità vicino a quella funzionalità o route e spostare in lib soltanto il codice condiviso in più punti.
Perché public e i file delle variabili d'ambiente appartengono alla directory radice del progetto?
public è la cartella nella directory radice del progetto per i file statici. I file collocati qui vengono serviti dal percorso radice; per esempio, a public/profile.png si fa riferimento come /profile.png. Offre un unico luogo per file serviti staticamente, come immagini e font. nextjs.org
Anche quando usi src, non dovresti pensare alla struttura come a uno spostamento di public in src/public. public rimane nella directory radice del progetto e anche package.json, next.config.js, tsconfig.json e .env.* vengono gestiti dalla radice. In particolare, poiché i file delle variabili d'ambiente locali come .env.local possono contenere segreti, è importante stabilire una regola operativa per non includerli nel controllo di versione. nextjs.org
Distinguere i ruoli delle risorse statiche e del codice sorgente dell'applicazione rende più semplici da interpretare i percorsi. I file in src/app sono codice che crea schermate o routing, mentre i file in public sono risorse statiche referenziate tramite URL. Anche quando la stessa immagine viene usata in una schermata, devi comprendere in modo diverso il metodo di distribuzione e il percorso di riferimento in base alla posizione del file.
In cosa differisce dalla struttura di Pages Router?
Pages Router funziona trattando i file nella directory pages come route. Per esempio, pages/index.tsx corrisponde a / e pages/about.tsx corrisponde a /about. Le convenzioni sui file riservati assegnano inoltre ruoli speciali a _app, _document, 404, 500 e altri. nextjs.org
È facile commettere errori se consideri App Router e Pages Router come approcci che differiscono soltanto nel nome della cartella. In App Router, file speciali come page.tsx, layout.tsx e route.ts sotto segmenti di cartella dividono le responsabilità, mentre i file ordinari non sono route per impostazione predefinita. In Pages Router, i file all'interno di pages sono collegati più direttamente alle route. nextjs.orgnextjs.org
Quando lavori con un progetto Pages Router esistente, dovresti rispettarne le attuali convenzioni basate su pages. Al contrario, quando avvii un nuovo progetto, basare la struttura su un esempio incentrato su App Router e aggiungere cartelle organizzative soltanto quando sono realmente necessarie riduce il sovraccarico. È importante non confondere le convenzioni dei due router nella stessa progettazione delle directory.
Quali criteri usare per scegliere una struttura per un progetto reale?
Per prima cosa, parti dalla struttura degli URL. Elenca i percorsi principali a cui accederanno gli utenti, poi determina quali layout condivisi usa ciascun percorso. Rappresenta il risultato tramite le cartelle in app e la posizione di page.tsx e layout.tsx. Usa i route group quando esiste un motivo chiaro per dividere aree di schermata o layout senza cambiare gli URL. nextjs.orgnextjs.org
In secondo luogo, valuta l'ambito del riutilizzo. Mantieni vicino a quella route il codice usato soltanto da una route. L'interfaccia utente usata da più route può essere spostata in un ambito più ampio, come components, e le utility usate da più funzionalità possono essere spostate in lib. Anziché generalizzare tutto fin dall'inizio, separare il codice quando emergono effettivi modelli di riutilizzo e modifica aiuta a ridurre le astrazioni non necessarie.
In terzo luogo, considera l'indipendenza delle funzionalità. Se funzionalità con aree di responsabilità e terminologia chiare, come account, amministrazione o ordini, diventano più grandi, confini di dominio come features possono essere utili. D'altra parte, quando le schermate sono poche e le distinzioni tra funzionalità sono deboli, possono essere sufficienti la colocation in app e un numero ridotto di cartelle condivise.
In quarto luogo, verifica il costo di individuazione per il team. Quando un nuovo membro del team cerca il codice per un determinato URL, dovrebbe poter seguire il percorso in app e trovare la pagina e l'implementazione dedicata. Anche i nomi e le posizioni di pulsanti o utility condivisi dovrebbero essere prevedibili. Una buona struttura deriva da questa prevedibilità più che da nomi di cartelle di tendenza.
Quali idee errate e insidie strutturali dovresti evitare?
La prima idea errata è che “creare una cartella crea immediatamente un URL”. In App Router, una cartella rappresenta un segmento, ma necessita di page.tsx o route.ts per diventare una pagina pubblica o un endpoint API. Questa regola consente ai file interni correlati di risiedere insieme nella cartella della route. nextjs.org
La seconda idea errata è che “senza una cartella con trattino basso, tutti i file interni sono esposti”. I file ordinari in App Router non sono route per impostazione predefinita. Un nome come _components non è una funzionalità di sicurezza obbligatoria; è uno strumento organizzativo che indica l'implementazione interna ed esclude la cartella dal routing. nextjs.org
La terza idea errata è che “i nomi dei route group sono inclusi anch'essi negli URL”. Il nome tra parentesi in (marketing) viene escluso dall'URL. Per via di questa praticità, devi assicurarti che gruppi diversi non creino lo stesso URL finale. Se suddividi più layout radice, la possibilità di un caricamento completo della pagina durante la navigazione tra gruppi è un altro elemento da considerare prima di progettare la struttura. nextjs.org
Infine, evita l'errore di mantenere directory app o pages corrispondenti sia nella radice sia in src durante l'introduzione di src. Poiché in questo caso la radice ha precedenza, può sembrare che il codice sorgente previsto non venga eseguito. Modificare una struttura non è semplicemente un'attività di aggiunta simultanea di cartelle; procedi verificando quale directory costituisce effettivamente la base per il routing. nextjs.org
Conclusione: lo standard riguarda i confini dei ruoli, non un elenco di cartelle
Il punto di partenza per la struttura di un progetto Next.js sono le convenzioni di routing definite da app e dai file speciali. In un nuovo progetto App Router, puoi rendere src/app il centro per URL, pagine e layout; mantenere public come cartella delle risorse statiche nella radice; e scegliere components, features, lib, hooks e types in base all'effettivo ambito di riutilizzo del codice e alla complessità del dominio. nextjs.orgnextjs.org
In definitiva, una buona struttura non è quella con il maggior numero di cartelle. È quella in cui i membri del team possono prevedere facilmente la posizione di una schermata per una particolare route, del codice dedicato a quella schermata e del codice condiviso tra più punti. Segui con precisione le convenzioni sui file di Next.js, quindi adatta gradualmente l'approccio organizzativo al di sopra di esse con l'evolvere del tasso di crescita e dei modelli di modifica del progetto.