React
Dynamic import e SSR: cosa succede
next/dynamic divide il bundle, non il rendering: il server renderizza comunque il componente. L'eccezione è ssr: false, che nell'App Router ha un vincolo.
In sintesi
- next/dynamic crea un chunk separato, non salta il rendering server.
- ssr: false vale solo dentro un client component.
- Un fallback senza altezza riservata produce layout shift.
- La tabella di next build dice se il taglio ha ridotto il peso.
Un dynamic import in Next.js taglia il bundle, non il rendering: il componente arriva in un chunk separato ma viene comunque renderizzato sul server. Cambia qualcosa solo con ssr: false, e nell'App Router quell'opzione ha un vincolo preciso.
Cosa fa next/dynamic
next/dynamic è un wrapper su React.lazy più Suspense. Il modulo viaggia in un chunk a parte e viene idratato quando serve, ma l'HTML della prima richiesta contiene già il markup, a meno che tu non disattivi il rendering server.
// app/(marketing)/page.tsx
import dynamic from "next/dynamic";
const Chart = dynamic(() => import("@/components/app/revenue-chart"), {
loading: () => ,
});
export default function Page() {
return ;
}
Il vincolo di ssr: false
Nell'App Router ssr: false non è ammesso in un server component: la build si ferma con un errore esplicito. Sposta la chiamata in un file con "use client" e il vincolo sparisce.
"use client";
import dynamic from "next/dynamic";
const MapPanel = dynamic(() => import("./map-panel"), { ssr: false });
export function MapShell() {
return ;
}
Quando serve davvero
- Librerie che leggono
windowal primo import: mappe, editor, canvas. - Widget pesanti sotto la piega, che nessuno vede prima del primo scroll.
- Componenti con dipendenze grandi e uso raro, come un editor di codice.
- Non serve per un componente da 2 KB: il chunk in più costa una richiesta.
Il costo che non vedi
Con ssr: false il primo paint mostra il fallback. Se il fallback non ha la stessa altezza del contenuto, la pagina salta: riserva lo spazio con min-height o con uno skeleton della stessa misura.
| Opzione | Server render | Primo paint | Uso |
|---|---|---|---|
dynamic(import) | Sì | Contenuto completo | Default |
ssr: false | No | Fallback | Solo in un client component |
React.lazy | Solo client component | Fallback | Fuori dall'App Router |
import() diretto | Sì | Contenuto completo | Moduli non React, caricamento su evento |
Per il fallback il catalogo ha skeleton; per i widget pesanti ci sono grafici e editor.
Il costo di una libreria è in bundle size di una libreria UI; l'ordine di caricamento in preload, prefetch e priority; l'attesa in streaming e Suspense. Confronto: Next.js vs Vite per un sito marketing.
npx next build
Limite noto: ssr: false sposta il componente sul client, quindi quel contenuto non è nel primo HTML e non legge dati dal server. Se deve essere indicizzato, resta sul server.
Domande frequenti
Un dynamic import toglie il rendering server?
No. Il componente resta nel server render: cambia il chunk, non l'HTML della prima risposta. Solo ssr: false salta il server.
Perché ssr: false dà errore nell'App Router?
Perché la pagina è un server component. Sposta la chiamata in un file con use client e l'errore sparisce.
Il dynamic import riduce il First Load JS?
Sì, se il componente è davvero raro. Il peso della rotta scende, ma il chunk resta nel build e viene scaricato quando il componente compare.
Posso usare React.lazy al posto di next/dynamic?
Solo dentro un client component. next/dynamic in più gestisce il fallback di caricamento e, se serve, disattiva il rendering server.
Come verifico che il taglio funzioni?
Guarda la tabella di next build: la colonna First Load JS per rotta deve scendere rispetto al build precedente.