È uscito il Corso Java Completo
Torna al blog

Cos'è Nuxt e cosa aggiunge a Vue

Cos'è Nuxt e cosa aggiunge a Vue: routing sui file, rendering lato server, auto-import di componenti e composables, e i moduli dell'ecosistema.

Edoardo Midali

Edoardo Midali

Developer · Content Creator

10 min di lettura

Sai già scrivere componenti Vue e ti funzionano. Poi arriva il progetto vero e ti accorgi che ti mancano le fondamenta: la navigazione tra pagine, i dati presi dal server, l'HTML che Google deve poter leggere, un posto dove mettere le API. Ognuna di queste cose, da sola, è una scelta e un incollaggio. Nuxt è la risposta già montata. In questo articolo trovi cosa aggiunge esattamente a Vue, come funzionano gli auto-import — la sua caratteristica più particolare — e quando invece ti sta solo intralciando.

Cos'è

Nuxt è il meta-framework di Vue: prende il framework che si occupa dell'interfaccia e ci mette intorno routing, rendering lato server, un backend e un sistema di moduli, con le scelte già fatte.

Sotto c'è Vue esatto, con la Composition API, i file .vue e ref. Quello che sai resta valido: Nuxt non cambia il modo in cui scrivi un componente, cambia tutto quello che sta attorno al componente.

Il parallelo con Next.js è preciso e conviene tenerlo a mente per tutto l'articolo: stesso ruolo, stesso posto nella pila, ecosistema diverso.

Cosa aggiunge a Vue

Il routing sui file

Non configuri rotte. Crei file dentro pages/ e il percorso sul disco diventa il percorso nell'URL.

pages/
  index.vue              →  /
  chi-siamo.vue          →  /chi-siamo
  blog/index.vue         →  /blog
  blog/[slug].vue        →  /blog/qualsiasi-cosa
app.vue                  →  l'involucro di tutto
layouts/default.vue      →  il layout comune

Le parentesi quadre sono il segmento dinamico, e lo leggi con useRoute(). La navigazione interna si fa con <NuxtLink>, che precarica la pagina di destinazione quando il link entra nella vista: il click risulta istantaneo perché il lavoro è già stato fatto.

Il rendering lato server

Per impostazione predefinita Nuxt genera l'HTML sul server e poi lo "risveglia" nel browser. Il visitatore vede il contenuto subito e i motori di ricerca ricevono una pagina piena, non un <div id="app"> vuoto.

I modelli disponibili — statico in fase di build, a ogni richiesta, con rigenerazione — sono gli stessi spiegati in SSG, SSR e ISR, e non li rifaccio qui. La cosa che conta in Nuxt è dove li decidi: non per progetto, ma per rotta, dentro nuxt.config.ts.

export default defineNuxtConfig({
  routeRules: {
    "/": { prerender: true },
    "/blog/**": { isr: 3600 },
    "/app/**": { ssr: false },
    "/api/**": { cors: true },
  },
});

Quattro righe e hai un sito con la home statica, il blog rigenerato ogni ora e l'area riservata che gira solo nel browser. È la parte di Nuxt fatta meglio.

I dati, con un composable solo

Il caricamento dati passa da useFetch (o useAsyncData, la versione più generica). Gira sul server durante la prima resa, il risultato viaggia insieme all'HTML e nel browser non viene rifatto.

<script setup>
const { data: articoli, pending, error } = await useFetch("/api/articoli");
</script>

<template>
  <p v-if="pending">Caricamento…</p>
  <p v-else-if="error">Qualcosa è andato storto</p>
  <ul v-else>
    <li v-for="a in articoli" :key="a.id">{{ a.titolo }}</li>
  </ul>
</template>

Niente onMounted con dentro una fetch, niente stato di caricamento scritto a mano, niente doppia richiesta. Il dettaglio importante: useFetch va chiamato al livello alto di <script setup>, non dentro un gestore di eventi. Per quello serve $fetch normale.

La cartella server/

Qui Nuxt fa una cosa che Vue da solo non ha mai fatto: ti dà un backend nello stesso progetto. Un file in server/api/ diventa un endpoint HTTP.

// server/api/iscritti.post.ts
export default defineEventHandler(async (event) => {
  const { email } = await readBody(event);

  if (!email?.includes("@")) {
    throw createError({ statusCode: 400, statusMessage: "Email non valida" });
  }

  await db.iscritti.create({ data: { email } });
  return { ok: true };
});

Il metodo HTTP sta nel nome del file (.post.ts, .get.ts). Sotto gira Nitro, il motore server di Nuxt, che è anche il pezzo responsabile del fatto che lo stesso progetto si pubblica su Node.js, su una piattaforma serverless o all'edge cambiando un'impostazione.

Se il concetto di endpoint è nuovo, il riferimento è cos'è una REST API.

Gli auto-import: la caratteristica distintiva

Questa è la cosa che sorprende chi arriva da Next.js, ed è anche quella su cui vale la pena spendere due minuti perché è comoda e insieme ambigua.

In Nuxt non scrivi gli import. Un componente dentro components/ è disponibile ovunque nel template. Una funzione esportata da composables/ è disponibile ovunque nello script. Le API di Vue — ref, computed, watch, onMounted — anche.

<script setup>
// nessun import, e funziona tutto
const conteggio = ref(0);
const doppio = computed(() => conteggio.value * 2);
const { utente } = useUtente(); // composables/useUtente.ts
</script>

<template>
  <CardProdotto :nome="utente.nome" />
  <!-- components/CardProdotto.vue -->
</template>

Il nome del componente si ricava dal percorso: components/prodotto/Card.vue diventa <ProdottoCard>. La regola vale anche per le cartelle annidate, e all'inizio genera confusione.

Perché è comodo. In un file Vue tipico gli import sono la metà delle prime venti righe e non dicono niente di interessante. Toglierli accorcia il file e rende indolore spostare il codice.

Perché va capito, e non solo subito. Tre conseguenze concrete:

  • Non vedi da dove arriva una cosa. Apri un file di un collega, trovi useCarrello(), e non c'è nessuna riga che ti dica dov'è definita. Devi cercarla. L'editor aiuta con "vai alla definizione", ma la lettura a occhio perde un appiglio.
  • Le collisioni di nomi sono silenziose. Due componenti con lo stesso nome in cartelle diverse, o un composable che si chiama come una funzione di un modulo: uno dei due vince, e capire quale costa tempo.
  • I tipi arrivano da file generati. Nuxt scrive le dichiarazioni in .nuxt/. Se cancelli quella cartella o non hai mai avviato il progetto, TypeScript segnala centinaia di errori su codice perfettamente corretto. La soluzione è nuxt prepare (o semplicemente avviare il progetto), ed è la prima cosa che fa perdere mezz'ora a chi inizia.

Gli auto-import si possono disattivare. Quasi nessuno lo fa, ma sapere che è una scelta e non una legge aiuta.

I moduli

Un modulo Nuxt è un pacchetto che si aggancia al progetto e configura da solo tutto quello che serve. Lo aggiungi alla lista e hai finito.

export default defineNuxtConfig({
  modules: [
    "@nuxt/image",
    "@nuxtjs/tailwindcss",
    "@nuxtjs/i18n",
    "@pinia/nuxt",
    "@nuxt/content",
  ],
});

Quella riga di Tailwind sostituisce la configurazione manuale di PostCSS, del file di stile e dei percorsi da scansionare. @nuxt/image ti dà <NuxtImg> che ridimensiona, converte in formati moderni e riserva lo spazio in pagina — la stessa roba che in Next.js fa <Image>, e che pesa parecchio sui Core Web Vitals.

Il valore vero non è tecnico ma di tempo risparmiato: nessuna decisione da prendere e nessun incollaggio da mantenere quando aggiorni le versioni.

Nuxt e Next.js a confronto

NuxtNext.js
Sotto c'èVueReact
RoutingCartella pages/ (o app/)Cartella app/
ImportAutomaticiLi scrivi tu
Backend nel progettoserver/api/ (Nitro)app/api/ + Server Action
Modelli di renderingPer rotta, in un file soloPer file, con opzioni sparse
Confine server/clientComponenti normali, useFetch sul serverServer e Client Component
HostingNeutrale, tanti adapterOttimale su Vercel
EcosistemaPiù piccoloEnorme

La differenza concettuale più grossa è il confine server/client. In Next.js è esplicito e devi ragionarci sopra: ogni componente è server finché non scrivi "use client". In Nuxt il modello resta quello classico dell'applicazione universale: il componente gira sul server per la prima resa e poi nel browser. È più semplice da imparare e ti risparmia una classe intera di errori, ma non riduce il JavaScript spedito allo stesso modo: in Nuxt il codice del componente arriva comunque al browser.

Sul resto, la simmetria è quasi noiosa. Se conosci uno, l'altro lo impari in un pomeriggio.

Errori comuni che fanno perdere ore

Chiamare useFetch nel posto sbagliato. Dentro un onClick non fa quello che pensi. Nei gestori di eventi si usa $fetch.

Toccare window o localStorage nello script. Il componente gira anche sul server, dove window non esiste, e ottieni un errore che nello sviluppo a volte non vedi. Va messo dentro onMounted, o dentro if (import.meta.client).

HTML diverso tra server e browser. Se durante la prima resa produci qualcosa che cambia — una data formattata con l'ora corrente, un numero casuale — il browser trova un albero diverso e si lamenta con un errore di hydration mismatch. Per i pezzi davvero solo-client c'è <ClientOnly>.

Confondere pages/ e app/. Nuxt 4 ha spostato il codice dell'applicazione dentro una cartella app/, ma quella cartella non è l'App Router di Next.js: è solo dove ora vivono pages/, components/ e layouts/. Metà delle guide online usa la struttura vecchia. Controlla la versione di quello che leggi.

Mettere segreti in runtimeConfig al posto sbagliato. Tutto quello che sta sotto la chiave public finisce nel browser. Le chiavi private vanno al livello superiore, e le legge solo il codice in server/.

Quando Nuxt è la scelta giusta e quando no

Ha senso quando il team fa già Vue. Questa è la ragione principale e non c'è bisogno di cercarne altre: se le persone scrivono Vue, il meta-framework naturale è Nuxt, punto. Passare a Next significherebbe cambiare anche framework.

Ha senso quando ti servono insieme contenuti indicizzabili e interattività vera: un e-commerce, un SaaS con pagine pubbliche e area riservata, un portale con account utente. E quando vuoi frontend e backend in un progetto solo.

Non ha senso per un sito vetrina o un blog. Porti un server, l'idratazione e un modello mentale non banale per un risultato che Astro produce più leggero. Il ragionamento è lo stesso di Next.js o Astro e vale identico qui.

Non ha senso per una dashboard tutta dietro login. Senza SEO il rendering lato server non ti regala niente: Vite più Vue è più veloce da sviluppare e da pubblicare.

E non ha senso sceglierlo "perché Vue è meglio di React" se stai imparando per lavorare. L'ecosistema Nuxt è buono ma più piccolo, le offerte di lavoro italiane pendono nettamente dall'altra parte, e quando cerchi la soluzione a un problema strano trovi meno risposte. È la stessa onestà che vale per Vue stesso.

In sintesi

Nuxt sta a Vue come Next.js sta a React: aggiunge routing sui file, rendering lato server, backend e ottimizzazioni a un framework che di suo fa solo l'interfaccia.

Gli auto-import sono la sua firma. Non scrivi import di componenti, composables e API di Vue. Comodissimo, a patto di sapere che nasconde la provenienza del codice e che i tipi dipendono da una cartella generata.

server/ ti dà le API nello stesso progetto, e Nitro ti lascia pubblicare lo stesso codice su Node, serverless o edge cambiando un'impostazione.

I modelli di rendering si scelgono per rotta in routeRules, ed è la parte più riuscita dello strumento.

Il criterio onesto: se il team fa Vue, Nuxt è la scelta giusta senza discussioni. Se stai scegliendo da zero con il lavoro in mente, la domanda vera non è Nuxt o Next, ma Vue o React.

Per le basi su cui è costruito, vedi cos'è Vue; per inquadrare la scelta nel progetto intero, come scegliere lo stack per una web app.