Turbopack in Next.js 16: Guida Completa alla Build e Migrazione da Webpack
Turbopack è il bundler predefinito di Next.js 16: benchmark reali su monorepo, come migrare loader Webpack, configurare Tailwind v4, e quando conviene ancora tornare a Webpack con --webpack.
Turbopack in Next.js 16 è il nuovo bundler predefinito, scritto in Rust, che sostituisce Webpack sia in next dev sia in next build, riducendo i tempi di cold start del 60–80% sui progetti reali che ho migrato quest'anno. Non è più una flag sperimentale: nella 16 stable, se non esporti webpack in next.config.ts, stai già usando Turbopack. In questa guida ti mostro i benchmark misurati su un monorepo di produzione, come migrare i loader Webpack personalizzati, dove Turbopack ancora scricchiola e quando conviene fare rollback esplicito a Webpack.
Turbopack è il bundler predefinito di Next.js 16 sia in sviluppo sia in build di produzione. Webpack resta disponibile ma richiede opt-in esplicito.
Sui progetti reali il cold start di next dev scende da 18–25s a 3–6s, e next build risulta tra il 40% e il 65% più veloce a parità di hardware.
La compatibilità con i loader Webpack non è totale: babel-loader, raw-loader e SVGR funzionano con configurazioni equivalenti in Turbopack, ma i loader che manipolano l'AST tramite plugin richiedono riscrittura.
In un monorepo Turborepo, Turbopack condivide la cache remota, però i task build devono essere ridefiniti per invalidare correttamente gli output .next/cache.
Alcune API rimangono non supportate: webpack() hook custom, plugin che modificano il chunking, e alcuni loader legacy CSS-in-JS (styled-jsx global styles funziona, Emotion SSR richiede workaround).
Cosa è Turbopack e perché Next.js 16 lo rende predefinito
Turbopack è un bundler incrementale scritto in Rust che condivide architettura con Turborepo e sfrutta lo stesso motore di caching a livello di funzione. La differenza fondamentale rispetto a Webpack non è "Rust vs JavaScript". Il punto vero è che Turbopack modella il grafo di dipendenze come un DAG di computazioni memoizzate, invalidando solo i nodi effettivamente cambiati. Quando salvi un file, non ricompila l'intera route: ricompila il singolo modulo e propaga solo agli import diretti che effettivamente lo consumano.
Nel ciclo di vita 15 → 16, Vercel ha promosso Turbopack in tre fasi: stable per next dev a fine 2024, beta per next build in 15.3, e default per entrambi in 16.0. Se il tuo next.config.ts non contiene webpack: (config) => …, la 16 sceglie Turbopack in modo silenzioso. Chi lavora su codebase legacy con customizzazioni Webpack pesanti riceve un warning esplicito al primo next build post-upgrade.
Per un team frontend maturo la scelta non è "usare Turbopack sì/no" ma "quanto della nostra pipeline attuale sopravvive alla migrazione". Nel mio caso (SaaS B2B con ~180k righe di TypeScript, design system interno con 60+ componenti, monorepo pnpm da 12 pacchetti) la migrazione ha richiesto due giorni di lavoro concentrato più una settimana di aggiustamenti in code review. La ricompensa è concreta: HMR sotto i 200ms anche sulle pagine di dashboard più complesse, dove Webpack ci teneva a 1.2–1.8s.
Turbopack vs Webpack: benchmark reali su un monorepo di produzione
I numeri che vedete sul blog di Vercel sono onesti, ma misurati su repository sintetici. Ecco cosa ho registrato su un progetto reale (MacBook Pro M3 Max, Next.js 16.0.4, pnpm 9, Node 22 LTS), comparando next build e next dev con e senza --webpack.
Metrica
Webpack (baseline)
Turbopack (Next.js 16)
Delta
Cold start next dev
22.4s
4.8s
−78%
HMR pagina dashboard
1.6s
180ms
−88%
Rebuild dopo node_modules pulito
3m 12s
1m 04s
−66%
Build di produzione (12 route)
2m 48s
1m 06s
−61%
Uso memoria di picco (build)
3.8 GB
2.1 GB
−45%
Bundle size (First Load JS)
187 kB
184 kB
−1.6%
Tree-shaking di lodash
Efficace con lodash-es
Efficace su entrambi
Pari
Da notare due cose. Prima: il bundle finale è praticamente identico. Turbopack non "produce codice migliore", produce lo stesso codice più in fretta. Se speravate in una riduzione magica del First Load JS, non la troverete. Seconda: l'uso di memoria è quasi dimezzato, cosa che nei runner di CI con 4 GB di RAM (tipico su GitHub Actions ubuntu-latest) trasforma next build da rischio OOM a operazione stabile.
Come abilitare (o disabilitare) Turbopack in build di produzione
In Next.js 16 non serve alcuna flag per usare Turbopack: è già attivo. Il tuo package.json resta quello classico:
Se invece hai un config Webpack che ti serviranno un altro paio di sprint per migrare, puoi forzare il vecchio bundler con la flag --webpack, disponibile sia per dev sia per build:
Vercel ha promesso di mantenere il supporto Webpack fino a Next.js 18, il che dà circa 12–15 mesi di tempo per completare la migrazione senza fretta. Nel frattempo, per esplicitare la scelta e non affidarti al comportamento default, puoi aggiungere in next.config.ts il blocco turbopack:
La proprietà turbopack è stabile in 16 (era experimental.turbo in 15). Se avete uno script di migrazione automatico nel repo, cercate stringhe come experimental.turbo e sostituitele. Next.js 16 emette warning ma non fallisce. Il tema è ampiamente documentato nella reference ufficiale della configurazione Turbopack.
Migrare i loader Webpack personalizzati a Turbopack
Questa è la parte dove la maggior parte dei team frontend si ferma. In Webpack, ogni file passa per una catena di loader; in Turbopack, il concetto esiste ma la sintassi è diversa e le API supportate sono un sottoinsieme. Ecco le tre categorie di loader e come ho gestito ciascuna.
Loader "trasformazione pura" (funzionano quasi sempre)
Loader come @svgr/webpack, raw-loader, yaml-loader, o i vari *-loader che leggono un file e ritornano una stringa/modulo funzionano in Turbopack riscrivendo la configurazione come rules:
Loader che accedono all'AST TypeScript (richiedono riscrittura)
Loader come ts-loader in modalità getCustomTransformers, o custom transformer che manipolavamo per instrumentare componenti, vanno riscritti come plugin SWC. Onestamente, questa è la parte dolorosa: SWC ha meno esempi pubblici di Babel, e scrivere plugin custom richiede Rust. Nel mio caso ho avuto due transformer da migrare, uno per estrarre stringhe i18n e uno per aggiungere data-testid automatici, e ho scelto di sostituirli con codemod eseguiti in pre-commit invece che a build time. Meno elegante, ma zero manutenzione Rust.
Plugin Webpack (non supportati)
Se il vostro next.config.ts contiene qualcosa come config.plugins.push(new MyCustomPlugin()), quel codice semplicemente non gira sotto Turbopack. Le funzioni webpack: (config) => config vengono ignorate quando Turbopack è attivo, e Next.js emette un warning al build. Le alternative pratiche sono tre: (1) portare la logica in un plugin SWC, (2) spostarla in uno script pre/post-build eseguito da Turborepo, (3) mantenere Webpack per quel progetto specifico via --webpack.
CSS Modules, PostCSS e Tailwind con Turbopack
CSS Modules, PostCSS e Tailwind funzionano in Turbopack senza configurazione aggiuntiva. Un styles.module.css importato da un componente viene compilato con lo stesso hash generator di Webpack, quindi non ci sono conflitti di nomi in ambienti misti (utile se in staging avete ancora una route Webpack e le altre Turbopack durante la migrazione).
Per Tailwind v4, che è il default consigliato dalla documentazione Next.js 16, la configurazione si riduce a un import CSS. Il vecchio tailwind.config.ts è opzionale:
PostCSS con plugin custom (per esempio postcss-preset-env o autoprefixer) è riconosciuto automaticamente se esiste un postcss.config.mjs nella root del progetto. L'unico gotcha che ho incontrato: i plugin PostCSS asincroni con side-effect (tipo quelli che scrivono file su disco per generare token del design system) devono essere spostati a build-time separato. Turbopack esegue PostCSS in worker paralleli e non garantisce l'ordine di esecuzione degli hook.
Per un approfondimento su come strutturare la pipeline di caching intorno a questi asset, ho scritto in dettaglio nella guida a caching e revalidazione in Next.js 16: le regole di invalidazione per gli asset statici sono cambiate proprio nella 16.
Turbopack in un monorepo Turborepo: cache e task pipeline
Turbopack e Turborepo hanno nomi simili ma sono strumenti diversi: Turbopack è il bundler dentro Next.js, Turborepo è l'orchestratore di task del monorepo. Insieme sono potenti, ma solo se il turbo.json è configurato per riflettere gli output corretti.
La riga chiave è "outputs": [".next/**", "!.next/cache/**"]. Se cachate anche .next/cache, Turborepo restituirà la cache Turbopack da un runner all'altro, il che sembra un vantaggio ma in realtà rompe l'invalidazione incrementale. La cache Turbopack contiene percorsi assoluti e assunzioni sul filesystem locale che non trasferiscono bene attraverso runner.
Con la Remote Cache di Turborepo configurata correttamente, il nostro pipeline CI è passato da 8m 42s a 2m 15s in cache hit, non per merito di Turbopack, ma per il fatto che ora next build viene skippato interamente per i pacchetti non modificati. Turbopack contribuisce quando la cache miss avviene: là il build da zero è più veloce, e quindi anche i falsi positivi di invalidazione fanno meno male.
Errori comuni Turbopack in Next.js 16 e come risolverli
"Module not found" su alias TypeScript che funzionavano in Webpack
Sintomo: Module not found: Can't resolve '@/components/Button'. Turbopack non legge automaticamente i paths di tsconfig.json quando la struttura è non-standard (per esempio quando il baseUrl punta a una directory che non è la root del pacchetto). Fix: aggiungere resolveAlias esplicito in next.config.ts, come nell'esempio della sezione 3.
SVG importati come URL invece che come componente React
Sintomo: import Logo from './logo.svg'; <Logo /> ritorna una stringa invece di un componente. Turbopack non applica SVGR per default. Fix: la regola *.svg mostrata sopra. Se avete file misti (alcuni SVG come componente, altri come URL) usate una convenzione di suffisso: logo.svg come URL, logo.icon.svg come componente, con due regole distinte in turbopack.rules.
Hot Reload che ricarica la pagina invece di preservare lo state
Sintomo: modifiche a un componente triggerano full page reload invece di Fast Refresh. La causa quasi sempre è questa: il componente non è un'export nominata pura, oppure il file esporta sia un componente sia non-componenti (funzioni utility, costanti). Ho beccato questo bug spedendo una release notturna e ci ho perso un'ora buona prima di capire. Fix: spostare le utility in un file separato, o esportare solo il componente come default. La regola di Fast Refresh non è cambiata rispetto a Webpack, semplicemente Turbopack la applica in modo più stretto.
Build fallisce con "webpack config is ignored"
Sintomo: warning che diventa error in CI. Se avete configurato process.env.CI come true e usate next build --no-warnings o simili, Turbopack può alzare l'errore. Fix: rimuovere la funzione webpack() dal config, o aggiungere --webpack allo script di build per confermare che quella configurazione vi serve.
Import di modulo CommonJS con default export che risulta undefined
Sintomo: pacchetti npm datati che usano module.exports = X ritornano undefined quando importati come import X from 'pkg'. Turbopack rispetta più strettamente la distinzione ESM/CJS. Fix: usare import * as X from 'pkg' oppure aggiungere il pacchetto a serverExternalPackages in next.config.ts se il problema si manifesta solo sul server.
Quando NON usare Turbopack (ancora)
Turbopack è pronto per la produzione nella grande maggioranza dei casi, ma ci sono tre scenari dove nella 16 continuo a usare --webpack:
Codebase con >5 plugin Webpack custom che non hanno equivalente SWC. Il costo di riscrittura supera il beneficio. Aspettate 1-2 minor release perché il plugin ecosystem SWC maturi.
App con Module Federation attivo. Turbopack non supporta ancora la Module Federation di Webpack 5. Se il vostro architetto ha strutturato l'app come micro-frontend federato, Webpack rimane l'unica opzione.
Pipeline CI che dipende da webpack-bundle-analyzer come single source of truth. Turbopack ha il suo analyzer ma i formati differiscono; se avete dashboard interne che consumano il JSON di webpack-stats, migrarle richiede lavoro.
Fuori da questi tre casi, il rapporto costo/beneficio favorisce Turbopack. Per il resto della migrazione consiglio anche di allineare l'architettura di deployment: se non l'avete già letta, la mia guida al self-hosting di Next.js 16 con Docker copre come costruire immagini che sfruttano gli output di Turbopack senza layer intermedi inutili. E se state usando proxy.ts per l'auth, la guida al middleware JWT con jose copre le sottigliezze del bundling edge di Turbopack, che sono diverse da quelle di Webpack in modo non ovvio.
Per approfondire la roadmap ufficiale, la sezione dei release notes di Next.js sul blog Vercel pubblica benchmark e piani di deprecation aggiornati a ogni minor.
Domande frequenti
Turbopack è pronto per la produzione in Next.js 16?
Sì, Turbopack è stable e default in Next.js 16 sia per next dev sia per next build. Vercel lo usa in produzione su vercel.com dal 2025. Le uniche eccezioni pratiche sono i progetti con plugin Webpack custom senza equivalente SWC o Module Federation attivo.
Come si disabilita Turbopack e si torna a Webpack?
Aggiungere la flag --webpack agli script next dev e next build in package.json. Il supporto Webpack è garantito fino a Next.js 18, quindi c'è tempo per completare la migrazione dei loader e plugin custom senza fretta.
Quanto è più veloce Turbopack rispetto a Webpack in build di produzione?
Nei miei benchmark su un monorepo reale con Next.js 16.0.4, next build è passato da 2m 48s a 1m 06s (−61%) e il cold start di next dev da 22s a 5s (−78%). L'uso di memoria di picco scende del 45%, cosa che stabilizza CI su runner con 4 GB di RAM.
I loader Webpack personalizzati funzionano con Turbopack?
I loader di trasformazione pura come @svgr/webpack, raw-loader e yaml-loader funzionano configurando turbopack.rules in next.config.ts. I loader che manipolano l'AST TypeScript vanno riscritti come plugin SWC, e i plugin Webpack veri e propri non sono supportati e vanno migrati o mantenuti su Webpack.
Turbopack riduce la dimensione del bundle finale?
No. Nei miei test la dimensione del First Load JS è praticamente identica (−1.6% marginale, dentro il rumore di misura). Turbopack produce lo stesso output di Webpack, solo più velocemente. Se cercate riduzioni di bundle serve intervenire sul codice, con dynamic imports, tree-shaking di librerie pesanti, code splitting per route.
Devo cambiare experimental.turbo in Next.js 16?
Sì. In Next.js 16 la configurazione si chiama turbopack (senza experimental). Il vecchio nome emette warning ma non fallisce ancora, vi conviene aggiornarlo prima della 17 dove diventerà errore. Le opzioni interne (rules, resolveAlias) restano compatibili.
Slot @, default.tsx e notazione (.), (..), (...): la guida pratica per costruire modali condivisibili con Parallel e Intercepting Routes in Next.js 16, con URL propri, back del browser e refresh gestito senza trucchi.
In Next.js 16 middleware.ts diventa proxy.ts e gira su Node.js. Ecco come costruire un flusso di autenticazione JWT con jose, dal matcher ai refresh token in cookie httpOnly, con verifica anche nei Server Components per mitigare la CVE-2025-29927.