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'accessoPerché fallisceCorrezione
Corpo del moduloviene eseguito al build e a ogni richiestasposta dentro una funzione chiamata dal client
Corpo del componente Servergira in Noderendi il file Client Component o passa il dato come prop
Corpo del componente Clientviene comunque prerenderizzato sul serverusa 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 window nei 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.