# Next.js in a monorepo: the structure that holds

A Next.js monorepo pays off when apps share components and config. The minimum structure with pnpm, the packages worth extracting and the real costs.

> Page: https://ui.eneawork.it/blog/nextjs-monorepo-struttura
> Markdown: https://ui.eneawork.it/blog/nextjs-monorepo-struttura.md

*2026-11-01*

Un monorepo Next.js conviene quando due applicazioni condividono davvero componenti, token e configurazione. La struttura minima è apps/ per i deploy, packages/ per il codice condiviso e un solo lockfile alla radice.

La domanda «Typescript pnpm monorepo, path aliases conflicts» vale 20 voti su Stack Overflow (letto il 2 novembre 2026 con la Stack Exchange API): fonte. Il catalogo EneUI misura 111.985 componenti, 1.556 blocchi, 12.808 icone da 1.472 librerie, misurati su /api/stats il 1 novembre 2026. Ogni comando qui sotto gira su Node 22 e pnpm 10, senza servizi esterni.

## La struttura minima

repo/ apps/ web/ # app principale, un deploy app/ next.config.ts package.json docs/ # seconda app, stesso design system packages/ ui/ # componenti condivisi config/ # eslint, tsconfig, tailwind lib/ # utility pure package.json pnpm-workspace.yaml turbo.json

Il workspace è dichiarato in pnpm-workspace.yaml; ogni cartella sotto apps/ e packages/ ha un package.json con un nome npm. Next.js non sa di essere in un monorepo: è il resolver dei moduli a collegare i pacchetti.

## Cosa condividere e cosa no

Un pacchetto che esporta file TSX va elencato in transpilePackages, altrimenti la build di Next ignora il file fuori dalla sua cartella. Se il pacchetto è usato anche fuori da Next, compilalo e pubblica il risultato.

// apps/web/next.config.ts import type { NextConfig } from "next"; const nextConfig: NextConfig = { transpilePackages: ["@repo/ui", "@repo/lib"], }; export default nextConfig;

## Il comando che smaschera il problema

# una sola versione di React in tutto il workspace pnpm why react # build della sola app: rivela gli import fuori dal pacchetto pnpm --filter @repo/web build

Se pnpm why react stampa due versioni, gli hook si rompono con «Invalid hook call» a runtime e il messaggio non dice perché. La causa è un pacchetto che dichiara React come dipendenza normale invece che come peer.

## Il record che uso come esempio

Il catalogo EneUI indicizza 111.985 componenti e 1.556 blocchi da 1.472 librerie (1 novembre 2026). Un blocco completo si installa come sorgente in packages/ui: dashboard-01, con la categoria dashboard e le primitive in card.

## Dove il monorepo costa

Limite noto: Next non risolve un pacchetto che espone solo ESM senza campo exports. La build fallisce con un errore di sintassi che punta al file sbagliato, e la correzione sta nel package.json del pacchetto.

Per la struttura della singola app vedi struttura App Router. Per la cache che attraversa le richieste i quattro livelli di cache; il confronto tra framework per un sito marketing è in Next.js vs Vite.

## FAQ

### Serve Turborepo per un monorepo Next.js?

No. Un workspace pnpm basta per due app e una decina di pacchetti. Turborepo aggiunge cache dei task e grafo delle dipendenze quando le build superano i cinque minuti.

### Dove metto il design system?

In packages/ui. Deve avere un nome npm, un package.json e nessuna dipendenza dall'app che lo consuma.

### Come evito due copie di React?

Dichiara React e React DOM come peerDependencies nei pacchetti condivisi e come dependencies solo nelle app. Poi verifica con pnpm why react.

### Meglio un'app unica o più app?

Una sola app finché la condivisione riguarda solo i componenti. La seconda app si aggiunge quando ha deploy, dominio o pubblico diversi.

### Il deploy su Vercel funziona senza configurazione?

Con pnpm sì, se il lockfile sta alla radice e il comando di build usa il filtro sul pacchetto. Con npm e cartelle annidate va impostata la root directory del progetto.
