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 window al 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.

OpzioneServer renderPrimo paintUso
dynamic(import)SìContenuto completoDefault
ssr: falseNoFallbackSolo in un client component
React.lazySolo client componentFallbackFuori dall'App Router
import() direttoSìContenuto completoModuli 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.