# server-only and client-only: the imports that break the build

Two one-line packages that turn a wrong import into a build error. Where to put them, what they block and what they leave unprotected.

> Page: https://ui.eneawork.it/blog/nextjs-server-only-client-only
> Markdown: https://ui.eneawork.it/blog/nextjs-server-only-client-only.md

*2026-11-01*

`server-only` e `client-only` trasformano un import sbagliato in un errore di build. Non cambiano il runtime: impediscono al bundler di mettere un modulo nel grafo che non gli appartiene.

## Come si installano

npm install server-only client-only

Sono due pacchetti vuoti con un `exports` condizionale: la condizione `react-server` risolve un file che lancia. Il resto del lavoro lo fa il bundler, che gira anche in CI.

## Il caso classico: un client che importa il database

// lib/db.ts import "server-only"; import { PrismaClient } from "@prisma/client"; const globalForDb = globalThis as unknown as { db?: PrismaClient }; export const db = globalForDb.db ?? new PrismaClient(); if (process.env.NODE_ENV !== "production") globalForDb.db = db;

La prima riga è tutta la protezione. Se un client component importa `lib/db.ts`, la build si ferma con un messaggio che nomina il modulo e il percorso. Senza quella riga il bundle proverebbe a includere il driver, e a volte ci riesce.

// lib/seed.ts import "server-only"; import { db } from "./db"; export async function seedDemo() { const count = await db.user.count(); if (count > 0) return { skipped: true }; await db.user.createMany({ data: [{ email: "demo@example.com" }] }); return { skipped: false }; }

Metti l'import in cima a ogni modulo che tocca segreti, filesystem, variabili d'ambiente private o driver di database. Anche nei file che sembrano innocui: uno script di seed importa il db, e prima o poi qualcuno lo importa da una pagina.

## `client-only` è più raro, ma serve

// components/local-time.tsx "use client"; import "client-only"; export function LocalTime({ iso }: { iso: string }) { const formatted = new Intl.DateTimeFormat("it-IT", { dateStyle: "full", timeStyle: "short", }).format(new Date(iso)); return {formatted}; }

Un componente che legge il fuso orario, il `localStorage` o una API del browser non può essere renderizzato sul server: il risultato cambierebbe tra server e client e React segnalerebbe un mismatch di idratazione.

## Direttive e pacchetti non sono la stessa cosa

Le direttive descrivono l'intenzione, i pacchetti la impongono. Una direttiva dimenticata produce un errore a runtime; il pacchetto lo produce alla build, quando costa meno.

## Il numero che conta

Il catalogo EneUI indicizza 111.985 componenti e 1.556 blocchi da 1.472 librerie, misurati il 1 novembre 2026 su `/api/stats`. Molti componenti del catalogo leggono il DOM e vogliono `client-only`: hover card, animated loader e form validation.

Un modulo che nomina un segreto prende `server-only` lo stesso giorno in cui viene scritto. Un modulo che nomina `window` prende `client-only`.

Il caso che rompe davvero è un hook importato da un server component: leggi server component che importa un hook client. Per l'errore `window is not defined` c'è finestra non definita in Next.js. Per il confine tra mutazioni e letture Route Handlers vs Server Actions. Per il confronto tra framework Remix vs Next.js.

Limite noto: `server-only` protegge il modulo, non il valore. Se un server component passa un segreto come prop a un client component, il pacchetto non dice nulla: il dato attraversa il confine nella risposta HTML.

## FAQ

### A cosa serve `server-only` se esiste già `"use server"`?

Non sono la stessa cosa. `"use server"` dichiara una funzione azione; `server-only` impedisce che un modulo server finisca nel bundle del client.

### L'errore compare in sviluppo o in build?

In entrambi, perché il bundler risolve la condizione `react-server` a ogni compilazione. In CI l'errore blocca il deploy, che è il momento giusto.

### Devo metterlo in ogni file che usa il database?

In ogni file che importa il driver o un segreto. Un wrapper che riesporta `db` ha bisogno della stessa riga, altrimenti riapre il buco.

### `client-only` serve se il file ha `"use client"`?

Sì, quando il modulo non può proprio girare sul server. `"use client"` permette al server di importarlo per il prerendering; `client-only` lo vieta.

### Funziona anche fuori da Next.js?

Sì, i pacchetti sono di React e si appoggiano alla condizione `react-server` del bundler. Con Vite o un altro bundler serve la stessa condizione.
