React
Struttura di un progetto Nuxt 3: cartelle e confini
In Nuxt 3 le cartelle sono API: pages, server e composables cambiano il comportamento. Una struttura che spiega dove mettere ogni file.
In sintesi
- Ogni cartella con nome riservato in Nuxt 3 attiva un comportamento: spostare un file cambia l’app.
- server/ non arriva al browser, quindi le chiamate al database stanno lì.
- components/ui/Button.vue si chiama UiButton: la sottocartella entra nel nome.
In Nuxt 3 la struttura del progetto è configurazione: ogni cartella con un nome riservato attiva un comportamento, quindi spostare un file cambia l’app. La regola utile è tenere pages/ per le rotte, server/ per il codice che il client non deve vedere e composables/ per lo stato condiviso.
Le cartelle che cambiano il comportamento
| Cartella | Cosa attiva | Confine |
|---|---|---|
| pages/ | Rotte generate dal file system | Client |
| server/api/ | Endpoint HTTP presi dal percorso | Server |
| server/utils/ | Funzioni importabili dagli handler | Server |
| composables/ | Auto-import con il nome del file | Client e server |
| middleware/ | Guardie eseguite prima del render | Client e server |
| plugins/ | Codice eseguito all’avvio | Dipende dal suffisso |
Il suffisso del plugin decide il confine: .client.ts gira solo nel browser, .server.ts solo su Node. Senza suffisso gira in entrambi, e un accesso a window rompe la build.
Un albero che regge
app.vue
pages/
index.vue
blog/[slug].vue
layouts/
default.vue
components/
ui/ # primitive: button, input, dialog
app/ # composizioni legate a una pagina
composables/
useSession.ts
server/
api/posts.get.ts
utils/db.ts
middleware/auth.ts
nuxt.config.ts
I componenti in sottocartella prendono il nome dalla cartella: components/ui/Button.vue diventa <UiButton />. È la causa più frequente di «componente non trovato» dopo uno spostamento.
Server e client: due mondi, un repository
// server/api/posts.get.ts
export default defineEventHandler(async (event) => {
const limit = Number(getQuery(event).limit ?? 10);
return await db.post.findMany({ take: limit });
});
Il codice in server/ non finisce nel bundle del browser. Una chiamata al database dentro un componente client non è un errore di sintassi: è un errore a runtime, quando la build prova a includerla.
Errori che costano tempo
- Chiamare
useFetchdentro un ciclo: una richiesta per ogni elemento e la stessa chiave di cache. - Mettere logica di dominio in
composables/e richiamarla daserver/: lo stesso file viene auto-importato in due contesti diversi. - Creare
utils/alla radice e aspettarsi l’auto-import nel server: gli handler usanoserver/utils/. - Dimenticare
nuxi preparedopo un clone: i tipi generati mancano e l’editor segnala errori falsi.
Lo stesso confine server/client vale negli altri framework: le form actions di SvelteKit funzionano proprio perché il codice di scrittura non arriva al browser.
Il limite noto
L’auto-import riduce le righe e nasconde le dipendenze. In un progetto con più team conviene disattivarlo per i componenti e importarli a mano, oppure accettare che la ricerca del simbolo passi dall’estensione dell’editor e non dal file.
Comandi
npx nuxi prepare
npx nuxi dev
Dal catalogo: shadcn-vue, l’icona Nuxt e il blocco logo Vue. Le guide: le librerie UI per Vue 3 e le guardie di Vue Router; il confronto: Vue contro React nel 2026.
Domande frequenti
Dove va app.vue in Nuxt 3?
Nella radice del progetto, accanto a nuxt.config.ts. Da Nuxt 4 il codice dell’app vive in app/ e app.vue si sposta dentro quella cartella.
La cartella pages è obbligatoria?
No. Senza pages l’app usa app.vue come unica pagina. La cartella serve quando vuoi le rotte generate dal file system.
Perché il mio componente non viene trovato?
Il nome include la sottocartella: components/ui/Button.vue si usa come UiButton. Sposta il file in components/ se vuoi il nome breve.
Posso usare server/utils in un componente?
No. Quel codice è compilato per il server e non entra nel bundle del browser; estrai la parte condivisa in utils/ o in un pacchetto.
Come disattivo l’auto-import?
In nuxt.config.ts imposta imports.autoImport a false per le API e components a false per i componenti, poi importa tutto a mano.