# Server Component che importa un hook client: l'errore e le tre uscite

Un Server Component non può usare stato, effetti o API del browser. L'errore è esplicito: la correzione è spostare il confine, non aggirarlo con un cast.

> Pagina: https://ui.eneawork.it/blog/server-component-importa-hook-client
> Markdown: https://ui.eneawork.it/blog/server-component-importa-hook-client.md

*2026-11-01*

Risposta breve: un Server Component non può usare hook. Aggiungi "use client" al file che contiene l'hook, oppure sposta l'hook nella foglia più piccola possibile. Non aggirare l'errore con un cast.

Il tag next.js conta 41.489 domande sulla Stack Exchange API al 31 ottobre 2026; il confine client/server è il tema che produce più errori dopo l'adozione dell'App Router. Il catalogo EneUI misura 111.985 componenti, 1.556 blocchi, 12.808 icone da 1.472 librerie, valori misurati e letti da /api/stats il 1 novembre 2026. Il comando di ricerca gira sulla cartella del progetto: nessun servizio esterno.

## Il messaggio esatto

You're importing a component that needs `useState`. This React hook only works in a Client Component but none of its parents are marked with a "use client" directive, so they're Server Components by default.

Il messaggio può nominare useEffect o useRef: cambia l'hook, non la causa. React segnala il primo che incontra nel modulo.

## Come funziona il confine

React compila i moduli uno alla volta e la direttiva marca un modulo, non un componente. Da quel punto in poi il bundler costruisce due grafi: uno per il server e uno per il client. Il grafo server non espone gli hook, perché la build risolve react con la condizione react-server, che contiene solo la parte server. Un file senza direttiva viene compilato in quel grafo, quindi useState non esiste come export e la build si ferma prima di eseguire il codice.

Nell'App Router il default è server, e vale anche per i figli: tutto ciò che un modulo client importa diventa client, mentre le props che ricevono da un genitore server restano renderizzate sul server. La conseguenza pratica è che il confine va tracciato dal basso: un solo "use client" in cima all'albero porta nel bundle ogni import di ogni discendente.

## La correzione minima

"use client"; import { useState } from "react"; export function Counter() { const [n, setN] = useState(0); return setN(n + 1)}>{n}; }

La direttiva vale per il file, non per il componente. Se il file importa una libreria grande, quella libreria entra nel bundle client: per questo la direttiva va messa il più in basso possibile.

## La correzione migliore: confine piccolo

// app/page.tsx — Server Component import { Counter } from "./counter"; export default async function Page() { const total = await getTotal(); return ; }

Il Server Component carica i dati e passa un valore serializzabile. Il Client Component gestisce l'interazione. Il confine è di un solo file e il resto della pagina resta sul server.

## Il passo diagnostico

# files that call a hook but never declare the boundary # Windows: run this in Git Bash grep -rlE "use(State|Effect|Ref|Reducer)\(" app components | xargs grep -L "use client"

L'elenco che esce contiene i candidati, non i colpevoli: un componente può usare un hook e ricevere l'interazione da un genitore client senza problemi. Il colpevole è il primo file della catena d'import che compare nel messaggio d'errore e che sta in quell'elenco. Parti da lì, non dalla pagina: se il file è una foglia di presentazione, sposta l'hook in un wrapper e lascia la foglia senza stato.

## Quando la correzione non funziona

"use client" non rende client i figli passati come children: un Server Component passato dentro un Client Component resta server. Se l'hook continua a fallire, il file che lo usa non è quello che hai marcato, oppure il componente arriva da un pacchetto che non dichiara la direttiva. In quel caso serve un wrapper client locale.

Un secondo errore comune è passare una funzione come prop a un Client Component: non è serializzabile. Passa dati, oppure usa una Server Action per la mutazione.

## Quando la correzione comune peggiora le cose

La scorciatoia più copiata è mettere la direttiva in cima a page.tsx o layout.tsx. Sembra funzionare finché la pagina è sincrona: appena c'è un await, il componente client non può essere asincrono e l'errore cambia forma invece di sparire. Nello stesso movimento tutto l'albero sotto il file diventa client, quindi ogni libreria importata entra nel bundle, e il costo lo paghi in produzione, dove non c'è più nessun avviso a ricordartelo.

## Un limite con un nome

Limite dichiarato: un modulo con "use client" non può esportare metadata né generateMetadata. Se sposti la direttiva nella pagina per far funzionare un hook, il titolo e la description smettono di essere generati sul server, e nessun test di rendering se ne accorge. La via d'uscita è tenere la pagina come server component e spostare solo l'interazione in un figlio.

## Come prevenirlo

## Dove continuare

Nel catalogo: componenti, sidebar con stato e blocchi. Le guide: App Router, struttura e confini e peso di una libreria UI; il confronto: copy-paste o headless.

## FAQ

### Perché l'errore nomina `useState` se uso `useEffect`?

React segnala il primo hook che incontra nel modulo. Il problema è lo stesso: il file non è un Client Component.

### `"use client"` rende client anche i figli?

No. I figli passati come `children` da un Server Component restano Server Component: il confine vale per il modulo che lo dichiara.

### Posso passare una funzione a un Client Component?

No, le props devono essere serializzabili. Passa dati o usa una Server Action per la mutazione.

### Perché il bundle è cresciuto dopo `"use client"`?

Il confine porta nel bundle client tutto ciò che il modulo importa. Se il file importa una libreria grande, quella libreria entra nel client.

### Serve `"use client"` in ogni file con un evento?

Sì, se il gestore è definito lì. Se il gestore arriva da un componente client già dichiarato, il file può restare server.
