Errori e fix
window is not defined in Next.js: dove nasce e come si corregge
Il server non ha `window`. L'errore compare quando un componente o un modulo legge API del browser durante il render o l'import. Tre correzioni, in ordine di costo.
In sintesi
- `window` esiste solo nel browser: qualsiasi accesso durante il render server-side fallisce.
- La prima correzione è spostare l'accesso in `useEffect`, che gira solo sul client.
- `dynamic(..., { ssr: false })` funziona solo dentro un Client Component.
Risposta breve: durante il render sul server window non esiste. L'errore compare ogni volta che un modulo legge window all'import o un componente lo legge nel corpo del render. Sposta l'accesso in useEffect, oppure importa il componente solo sul client.
Il messaggio esatto
ReferenceError: window is not defined
at Object. (/app/lib/analytics.js:3:12)
at Module._compile (node:internal/modules/cjs/loader:1358:14)
La riga indicata è quasi sempre al livello superiore del file, non dentro una funzione. È il dettaglio che distingue un errore di rendering da un errore di import.
Perché succede
Next.js esegue il codice dei Server Component e il prerender delle pagine in Node. Node non ha window, document, localStorage né navigator. Un modulo che inizializza un client analytics al momento dell'import viene eseguito anche in quel contesto.
| Dove sta l'accesso | Perché fallisce | Correzione |
|---|---|---|
| Corpo del modulo | viene eseguito al build e a ogni richiesta | sposta dentro una funzione chiamata dal client |
| Corpo del componente Server | gira in Node | rendi il file Client Component o passa il dato come prop |
| Corpo del componente Client | viene comunque prerenderizzato sul server | usa un effetto |
La correzione, tre livelli
Se il valore serve solo dopo l'interazione, useEffect è sufficiente: gira solo nel browser e non partecipa al prerender.
"use client";
import { useEffect, useState } from "react";
export function Width() {
const [width, setWidth] = useState(0);
useEffect(() => {
const onResize = () => setWidth(window.innerWidth);
onResize();
window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize);
}, []);
return {width || "—"};
}
Se il componente intero dipende dal browser, una mappa, un editor, una canvas, caricalo senza SSR. In App Router ssr: false è consentito solo dentro un Client Component.
"use client";
import dynamic from "next/dynamic";
const Editor = dynamic(() => import("./editor"), { ssr: false });
export function EditorShell() {
return ;
}
Se l'errore arriva da una libreria, la via più pulita è una guardia esplicita più un import dinamico: il modulo non viene valutato finché non serve.
Quando la correzione non funziona
typeof window !== "undefined" evita il crash ma non è una soluzione: il valore calcolato sul server resta diverso da quello del client, e ottieni un hydration mismatch al posto del ReferenceError. Allo stesso modo, spostare l'accesso in una funzione che viene chiamata durante il render non cambia nulla: conta quando viene eseguita, non dove è scritta.
Come prevenirlo
- Tieni le API del browser in un solo modulo, sempre importato dinamicamente.
- Aggiungi la regola lint che vieta
windownei file senza"use client". - Testa la build, non solo il dev server: il prerender è il punto in cui il problema emerge.
Dove continuare
Per partire da codice già eseguibile: componenti React, una dashboard con shell e tutti i blocchi con preview. Le guide collegate sono App Router, struttura e confini client/server e quanto pesa una libreria UI; il confronto utile è copy-paste o headless.
Domande frequenti
Perché l'errore appare solo al build?
Durante `next build` le pagine vengono prerenderizzate: il codice che legge `window` al livello del modulo viene eseguito in Node.
`typeof window !== "undefined"` basta?
Evita il crash, ma non è una correzione: il valore calcolato sul server resta diverso da quello del client e può produrre un hydration mismatch.
Posso usare `useEffect` per leggere `window`?
Sì, gli effetti girano solo nel browser. Inizializza lo stato con un valore neutro e aggiornalo nell'effetto.
Perché `ssr: false` dà errore in un Server Component?
Next.js 15 non permette `ssr: false` in un Server Component. Metti `"use client"` nel file che chiama `dynamic`, oppure avvolgi il componente in un client wrapper.
Vale anche per `document` e `localStorage`?
Sì. Tutte le API del browser mancano in Node: la regola è la stessa, cambia solo il nome.