React Server Components in produzione: 6 errori da evitare nel 2026
Il 45% degli sviluppatori React usa i Server Components, ma nello State of React 2025 sono la terza funzionalità meno gradita. Sei errori spiegano il divario.
In questo pezzo
Un team porta un'applicazione React sull'App Router, sposta quasi tutto l'albero su Server Components e rilascia. Il bundle client cresce di 40 kB invece di calare. Nessuno sa spiegare perché. Quasi sempre la causa è una riga sola: un 'use client' in cima a un layout condiviso o a un file barrel, che si trascina nel browser ogni modulo importato sotto di lui.
È un errore che si ripete. I Server Components sono disponibili in versione stabile da React 19, uscito a dicembre 2024, e l'adozione c'è: nella survey State of React 2025 il 45% di chi ha risposto dichiara di averli usati. Il giudizio però non segue. Nella stessa survey i Server Components risultano la terza funzionalità meno gradita e le Server Functions la quarta. La rilevazione è andata dal 19 novembre 2025 al 13 gennaio 2026 e ha raccolto 3.760 risposte.
La distanza fra adozione e gradimento ha una spiegazione precisa. RSC non è un'ottimizzazione da attivare: è un confine dentro il grafo dei moduli. Chi lo tratta come un interruttore inciampa in sei problemi ricorrenti.
Perché il bundle client cresce dopo il passaggio ai Server Components
Perché 'use client' segna un confine, non un singolo componente. La documentazione React lo dice chiaramente: la direttiva dichiara il punto di ingresso del grafo dei moduli client, e ogni modulo importato da quel file, componenti figli compresi, finisce nel bundle del browser. Non serve ripeterla più in basso nell'albero, ed è proprio questa la trappola. Una direttiva su un barrel condiviso tipo components/index.ts porta nel browser tutto il barrel.
La direttiva va messa dove serve davvero l'interattività: sul bottone, non sulla pagina che lo contiene. Gli helper lato server vanno in file che non stanno mai sotto un punto di ingresso client. I barrel nelle cartelle di UI condivisa vanno eliminati, perché un barrel garantisce che il confine finisca troppo in alto. Poi si verifica: next build stampa il First Load JS per ogni route e dopo una migrazione riuscita quel numero scende. Se sale, il confine è nel posto sbagliato.
Gli await in sequenza trasformano una pagina in una catena di attese
Con i Server Components si scrive await dentro il componente. Si legge bene e nasconde un costo. Se un componente padre aspetta prima di renderizzare figli che a loro volta caricano dati, le richieste partono una dopo l'altra e il time to first byte diventa la somma di tutti i passaggi invece del più lento. LogRocket lo documenta come l'errore di performance più frequente con RSC su Next.js.
Due soluzioni, e non sono intercambiabili. Se le chiamate sono indipendenti, si spostano nel componente padre e partono insieme con Promise.all. Se una è lenta e le altre no, si fa il contrario: ogni fetch va in un componente fratello, avvolto in <Suspense>, così React manda i risultati in streaming man mano che arrivano e una query di reportistica da 900 ms non blocca un header da 20 ms.
Il Context non attraversa il confine, e la scorciatoia annulla la migrazione
L'incompatibilità con il Context API è la lamentela più citata nell'analisi dello State of React 2025, con 59 menzioni. Il motivo è strutturale: un Server Component viene renderizzato una volta sola, sul server, senza stato e senza effetti, quindi useContext, useState e useEffect non ci sono per scelta di progetto.
La scorciatoia più diffusa è avvolgere l'applicazione in un provider client dentro il layout radice. Il Context torna a funzionare e nel frattempo l'intero sottoalbero torna sul client, il che cancella il motivo della migrazione. Il Context è un meccanismo lato client e va tenuto dentro i sottoalberi client. Sul server i dati si passano come prop, i valori legati alla richiesta come cookie, header e parametri di route si leggono dove servono, e le letture ripetute nella stessa richiesta si deduplicano con cache() di React. Abbiamo trattato la questione di dove mettere il confine nell'albero decisionale fra server e client.
Gli errori di serializzazione si vedono a runtime, non in compilazione
Ogni prop che passa da un Server Component a un Client Component viene serializzata nel payload RSC. Oggetti semplici, array, primitivi, Promise e Server Functions passano senza problemi. Le istanze di classe e le funzioni normali no. I casi tipici sono i modelli di un ORM, i tipi dei driver di database come Decimal, e le callback passate giù per abitudine.
TypeScript non intercetta niente di tutto questo. L'errore arriva nella console del browser la prima volta che quel ramo viene renderizzato, quindi in staging o direttamente in produzione. Al confine conviene convertire in oggetti semplici e tipizzarli come DTO espliciti, così la forma dei dati che attraversa diventa una decisione e non una fuga.
In Next.js 16 il caching è cambiato sotto i piedi
Next.js 16 ha introdotto i Cache Components. Con cacheComponents: true non viene messo in cache niente per impostazione predefinita e si sceglie cosa mettere con la direttiva 'use cache', che sostituisce unstable_cache e toglie il caching implicito delle fetch. La documentazione sul caching descrive il nuovo comportamento: i dati letti al momento della richiesta sono dinamici finché non li marchi come cacheati.
Chi ha imparato RSC su Next.js dalla 13 alla 15 si porta dietro intuizioni ormai sbagliate. Pagine che credeva statiche vengono renderizzate a ogni richiesta, i tempi di risposta si muovono e i costi di infrastruttura si muovono con loro. Dopo l'aggiornamento va controllata route per route, senza fidarsi del modello mentale precedente, e il controllo va abbinato a una struttura che manda in streaming. Il Partial Prerendering è il meccanismo che rende utile una shell messa in cache in modo esplicito.
I Server Components asincroni restano fuori dai test unitari
La guida ufficiale di Next.js su Vitest lo scrive senza giri di parole: i Server Components asincroni non sono supportati da Vitest e per quelli si consigliano test end-to-end. Le lacune sui test hanno raccolto 24 menzioni nelle risposte dello State of React 2025. Jest ha lo stesso limite.
La divisione che funziona è a due livelli. Con i test unitari si coprono i componenti sincroni, le Server Functions trattate come funzioni normali, la validazione degli schemi e la logica pura. Con Playwright si coprono i Server Components asincroni, il middleware, i cookie e il routing. Chi salta questo passaggio scopre il buco quando la copertura sembra buona e una regressione sul caricamento dati arriva comunque in produzione.
Come è fatta una migrazione che funziona
- Si parte da una misura. First Load JS per route, LCP e TTFB sulle tre route con più traffico. Senza numeri prima, dopo non si può dichiarare niente.
- Si spostano le foglie, non le radici. Prima i componenti che si limitano a leggere dati. I sottoalberi interattivi restano fermi finché il percorso dei dati non è stabile.
'use client'va spinto in basso. Una direttiva per ogni isola interattiva, mai su un layout o su un barrel.- Il confine ha un contratto. DTO espliciti per ogni prop che passa a un componente client, così la serializzazione si controlla in review e non in produzione.
- Parallelo o streaming, si decide route per route.
Promise.allper le chiamate indipendenti, un confine Suspense quando una chiamata è lenta. - Si misura di nuovo. Se peso del bundle, LCP e TTFB non migliorano, la migrazione ha portato solo complessità. Quella route torna indietro.
Il guadagno si concentra sulle route ricche di contenuto e indicizzate: pagine di marketing, cataloghi, documentazione, articoli. Sono quelle dove mandare meno JavaScript diventa una pagina misurabilmente più veloce e dove si muovono i Core Web Vitals.
Quando conviene lasciar perdere RSC
Tre casi in cui lo scambio non conviene. Una dashboard dietro autenticazione, molto interattiva e senza esposizione sui motori di ricerca, spende il budget di complessità per pochissimo, perché quasi ogni sottoalbero finisce comunque sul client. Un'applicazione piccola con un backend veloce guadagna poco spostando il rendering su un server che adesso deve esistere e va pagato. E un team senza una strategia di test sui componenti asincroni, su un prodotto dove le regressioni costano, si compra un buco noto nella rete di sicurezza.
RSC è un buon punto di partenza per applicazioni nuove con una parte pubblica importante. È un punto di partenza discutibile su un'applicazione esistente che funziona, dove il costo della migrazione è reale e il guadagno misurato spesso sfiora lo zero. La decisione si prende route per route, con i numeri in mano, e le route dove i numeri dicono no restano come sono.
Domande frequenti
I React Server Components rendono più veloce un'applicazione per definizione?+
No. Tolgono codice dei componenti dal bundle client, e questo aiuta le pagine che spedivano JavaScript inutile. Non fanno niente per una query lenta e aggiungono un passaggio sul server che una pagina statica non aveva. Sulle route pubbliche ricche di contenuto lo scambio di solito conviene. Su una dashboard interattiva, dove quasi tutto resta client, il guadagno misurato sfiora lo zero. Si registrano First Load JS, LCP e TTFB prima della migrazione e si confrontano dopo, route per route.
Posso ancora usare Redux o Zustand con i Server Components?+
Sì, dentro i sottoalberi client. Uno store è stato lato client e vive dietro un confine "use client" come qualsiasi altro codice interattivo. Cambia dove si mette il provider. Avvolgere il layout radice in un provider funziona e riporta tutto l'albero sul client, il che annulla il vantaggio. Il provider va messo attorno all'isola interattiva che ne ha bisogno, i dati dal server entrano in quell'isola come prop e il resto della route resta sul server.
Conviene migrare all'App Router un'app Next.js che gira sul Pages Router nel 2026?+
Solo route per route, e solo dove i numeri lo giustificano. Il Pages Router funziona e continua a ricevere correzioni. Riscrivere per intero un'applicazione che gira significa comprarsi un nuovo modello di caching, un buco nei test sui componenti asincroni e una nuova categoria di bug di serializzazione, in cambio di una riduzione del bundle che si può stimare in anticipo. Si parte dalle due o tre route pubbliche che portano traffico organico, si misura e sul resto si decide dopo.
Come si intercettano gli errori di serializzazione prima della produzione?+
Tipizzando il confine. Per ogni prop che passa da un Server Component a un Client Component si definisce un DTO esplicito, lo si costruisce con una funzione di mapping e non si passa mai direttamente un'entità dell'ORM o un tipo del driver. Così un errore invisibile a runtime diventa una domanda in code review. Poi si aggiunge un passaggio Playwright sulle route che renderizzano quei rami, perché un test unitario sincrono non esegue il render asincrono lato server dove l'errore compare.
Servizi correlati
Studio
Inizia un progetto.
Scriviamo riguardo a ciò che costruiamo. Raccontaci cosa vuoi costruire tu.