Come migliorare le performance di un sito Next.js

Mettiti comodo/a, che oggi parliamo di qualcosa che sta a cuore a chiunque lavori sul web, specialmente se usi Next.js: come far sfrecciare il tuo sito!

Sai quella sensazione quando apri una pagina e… aspetti? E aspetti ancora? Ecco, non è proprio il massimo, vero? Un sito lento è frustrante per gli utenti, penalizzato da Google e, diciamocelo, un po’ imbarazzante per chi l’ha costruito. Ma la buona notizia è che con Next.js abbiamo un sacco di frecce al nostro arco per ottimizzare le performance. Non si tratta solo di “fare più veloce”, ma di creare un’esperienza utente fluida, piacevole, che faccia venire voglia di tornare.

Allora, sei pronto/a a trasformare il tuo sito Next.js da “carino ma lentuccio” a “wow, che velocità!”? Iniziamo!

1. Scegliere la Strategia di Rendering Giusta: Il Cuore di Next.js

Next.js è fantastico perché ci dà diverse opzioni su come e quando generare le nostre pagine HTML. Scegliere quella giusta per ogni tipo di pagina è fondamentale. Vediamole in modo semplice:

  • Static Site Generation (SSG): Immagina di preparare una torta in anticipo. La pagina HTML viene generata una volta sola al momento della build. È velocissima perché il server deve solo inviare un file statico già pronto. Ideale per pagine che non cambiano spesso: articoli di blog, pagine informative, landing page marketing.
  • Come si fa (Pages Router)? Usi la funzione getStaticProps nella tua pagina per recuperare i dati necessari alla build.
  • Come si fa (App Router)? Per impostazione predefinita, i componenti senza fetch dinamici o funzioni dinamiche sono resi staticamente. Se usi Workspace, Next.js cerca di cachare i dati per ottenere un comportamento simile a SSG.
  • Vantaggio: Velocità imbattibile, ottimo per la SEO, riduce il carico sul server.
  • Server-Side Rendering (SSR): Qui la torta viene preparata al momento, ogni volta che qualcuno la chiede. La pagina HTML viene generata sul server ad ogni richiesta. Utile quando la pagina mostra dati che cambiano frequentemente o dipendono dall’utente loggato (es. una dashboard personale, risultati di ricerca).
  • Come si fa (Pages Router)? Usi la funzione getServerSideProps.
  • Come si fa (App Router)? Puoi forzare il rendering dinamico usando funzioni come headers() o cookies(), oppure configurando Workspace per non usare la cache (cache: ‘no-store’).
  • Vantaggio: Mostra sempre i dati più aggiornati.
  • Svantaggio: Più lento di SSG perché il server lavora ad ogni richiesta. Time To First Byte (TTFB) più alto.
  • Incremental Static Regeneration (ISR): È un mix intelligente. La pagina viene generata staticamente (come SSG), ma Next.js la rigenera in background ogni tot tempo (es. ogni ora) o quando specifici dati cambiano (On-demand ISR). Così hai la velocità dello statico con aggiornamenti periodici. Perfetto per cose come una homepage di un e-commerce con prodotti che cambiano, ma non ogni secondo.
  • Come si fa (Pages Router)? In getStaticProps, restituisci una prop revalidate con il numero di secondi dopo cui rigenerare (es. revalidate: 3600 per un’ora).
  • Come si fa (App Router)? Puoi impostare l’opzione revalidate direttamente nella chiamata Workspace o a livello di route segment.
  • Vantaggio: Il meglio dei due mondi: veloce come SSG, ma con dati aggiornati.
  • Client-Side Rendering (CSR) & React Server Components (RSC – App Router):
  • CSR (tradizionale): La pagina HTML iniziale è quasi vuota, e JavaScript si occupa di caricare i dati e costruire l’interfaccia nel browser dell’utente. Utile per parti molto interattive di un’app (es. un editor complesso). In Next.js si usa spesso all’interno di pagine SSG/SSR per componenti specifici.
  • React Server Components (RSC – nel nuovo App Router): Questa è la grande novità! I componenti possono essere renderizzati solo sul server, senza inviare il loro JavaScript al client. Questo riduce drasticamente il bundle size lato client. Sono l’impostazione predefinita nell’App Router. Puoi poi definire esplicitamente i “Client Components” (quelli che usano useState, useEffect, event handlers) con la direttiva “use client” all’inizio del file.
  • Vantaggio (RSC): Meno JavaScript sul client = caricamento più veloce, migliore interattività iniziale.

Consiglio pratico: Parti da SSG/RSC come default. Chiediti: “Questa pagina deve essere generata ad ogni richiesta?”. Se la risposta è no, vai di statico o ISR. Usa SSR/Client Components solo quando strettamente necessario.

2. Ottimizzazione Immagini: Mai più Immagini Pesanti!

Le immagini sono spesso le maggiori responsabili di siti lenti. Ma Next.js ci dà una mano enorme con il suo componente <Image> (da next/image). Perché usarlo?

  • Ottimizzazione Automatica: Ridimensiona, ottimizza e serve le immagini in formati moderni come WebP o AVIF (se il browser li supporta), che sono molto più leggeri dei classici JPEG/PNG a parità di qualità.
  • Lazy Loading di Default: Le immagini vengono caricate solo quando stanno per entrare nel viewport (cioè quando l’utente sta per vederle). Questo velocizza il caricamento iniziale della pagina.
  • Prevenzione del Layout Shift: Richiede le dimensioni (width e height) per riservare lo spazio corretto prima che l’immagine carichi, evitando fastidiosi “salti” del layout (Cumulative Layout Shift – CLS, un Core Web Vital).

Come usarlo:

import Image from 'next/image'; import profilePic from '../public/me.png'; // Importa l'immagine se è locale function MyComponent() {   return (     <div>       <h1>Ciao!</h1>       { }       <Image         src={profilePic}         alt="Foto profilo"         width={500}         height={500}                       />       { }       <Image         src="https://images.unsplash.com/photo-12345"         alt="Descrizione immagine esterna"         width={800}         height={600}                       />     </div>   ); } export default MyComponent;

3. Code Splitting Automatico e Dynamic Imports: Carica Solo Quello Che Serve, Quando Serve

Next.js è intelligente: fa code splitting automaticamente a livello di pagina. Significa che quando visiti una pagina, carichi solo il codice JavaScript necessario per quella pagina, non per l’intero sito. Figo, no?

Ma possiamo fare di più! Se hai componenti pesanti (es. una libreria di grafici, un editor complesso, un modale) che non sono necessari subito al caricamento della pagina, puoi usare i Dynamic Imports (next/dynamic).

Come si fa:

import { useState } from 'react'; import dynamic from 'next/dynamic'; // Importa il componente pesante in modo dinamico const HeavyChartComponent = dynamic(() => import('../components/HeavyChart'), {   loading: () => <p>Caricamento grafico...</p>, // Opzionale: mostra un messaggio di caricamento   ssr: false // Opzionale: se il componente funziona solo sul client (es. usa 'window') }); function MyDashboard() {   const [showChart, setShowChart] = useState(false);   return (     <div>       <h2>Dashboard</h2>       <button onClick={() => setShowChart(true)}>Mostra Grafico Dettagliato</button>       { }       {showChart && <HeavyChartComponent />}     </div>   ); } export default MyDashboard;

In pratica, il codice di HeavyChartComponent viene messo in un file JavaScript separato (un “chunk”) e scaricato dal browser solo quando showChart diventa true. Questo riduce il peso del bundle JavaScript iniziale della pagina MyDashboard.

4. Analizza il Tuo Bundle: Scopri Chi Pesa Troppo!

A volte, senza accorgercene, includiamo librerie enormi o codice duplicato. Come scoprirlo? Usando un analizzatore di bundle! Una libreria popolare è @next/bundle-analyzer.

Come si usa (in breve):

  1. Installala come dipendenza di sviluppo

    npm install --save-dev @next/bundle-analyzer # o yarn add --dev @next/bundle-analyzer # o pnpm add --save-dev @next/bundle-analyzer
  2. Configura il tuo next.config.js:

    // next.config.js const withBundleAnalyzer = require('@next/bundle-analyzer')({   enabled: process.env.ANALYZE === 'true', }); module.exports = withBundleAnalyzer({   // La tua configurazione Next.js qui...   reactStrictMode: true, });
  3. Lancia la build con la variabile d’ambiente ANALYZE=true:

    ANALYZE=true npm run build # o ANALYZE=true yarn build # o ANALYZE=true pnpm build
  4. Dopo la build, si apriranno automaticamente nel browser delle mappe interattive che ti mostrano quali moduli occupano più spazio nel tuo bundle JavaScript (sia lato server che client).

Guardando queste mappe, potresti scoprire che una piccola utility che usi poco sta importando un’intera libreria gigante. A quel punto puoi cercare alternative più leggere o valutare se puoi importare solo la parte specifica che ti serve (tree-shaking permettendo).

5. Ottimizzazione dei Font: Stop ai Salti di Testo!

I font web sono belli, ma possono causare due problemi:

  1. Flash of Unstyled Text (FOUT): Il testo appare brevemente con un font di sistema prima che il font web venga caricato.
  2. Layout Shift: Se il font di sistema e quello web hanno dimensioni diverse, il testo “salta” quando il font web carica, peggiorando il CLS.

Next.js ci aiuta con next/font. Questo modulo ottimizza i font (sia Google Fonts che font locali) direttamente al momento della build.

Come si fa (con Google Fonts):

// In _app.js (Pages Router) o layout.js (App Router) import { Inter } from 'next/font/google'; // Configura il font (specifica subset, pesi, ecc.) const inter = Inter({ subsets: ['latin'] }); function MyApp({ Component, pageProps }) { // O il tuo Layout nell'App Router   return (     <main className={inter.className}> { }       <Component {...pageProps} />     </main>   ); } // Nell'App Router sarebbe simile nel file layout.js radice: // export default function RootLayout({ children }) { //   return ( //     <html lang="en" className={inter.className}> //       <body>{children}</body> //     </html> //   ); // }

Vantaggi:

  • Self-hosting automatico: I file dei font vengono scaricati e serviti direttamente dal tuo dominio, migliorando privacy e performance (niente richieste extra a Google).
  • Preload: Next.js aggiunge i link preload corretti nell’ <head>.
  • Nessun Layout Shift: Vengono impostate automaticamente le proprietà CSS size-adjust per minimizzare i salti di layout.

6. Caching Intelligente: Non Rifare Lavoro Inutile

Next.js ha diversi livelli di caching:

  • Build Cache: Durante la build SSG, i risultati di getStaticProps e le pagine generate vengono cachati. Le build successive saranno più veloci se il codice non è cambiato.
  • Data Cache (App Router): Le chiamate Workspace nell’App Router vengono automaticamente cachate per impostazione predefinita. Puoi controllare finemente il comportamento della cache (durata, rivalidazione).
  • Full Route Cache (App Router): Next.js cerca di cachare aggressivamente i segmenti di route renderizzati staticamente sul server, inclusi i risultati delle RSC e l’HTML.
  • Router Cache (Client-side): Il client memorizza i payload delle navigazioni recenti per rendere i passaggi tra pagine pre-visitate quasi istantanei.

Oltre a questo, se fai il deploy su piattaforme come Vercel (i creatori di Next.js), benefici automaticamente di una CDN (Content Delivery Network) globale. I tuoi asset statici (JS, CSS, immagini, pagine SSG) vengono copiati su server sparsi per il mondo, così gli utenti li scaricano dal server più vicino a loro, riducendo la latenza.

7. Gestire Script di Terze Parti: Con Ordine!

Google Analytics, chat di supporto, script di advertising… spesso dobbiamo includere script esterni. Questi possono però bloccare il rendering o appesantire la pagina. Next.js offre il componente <Script> (da next/script) per gestirli meglio.

Come si usa:

import Script from 'next/script'; function MyPage() {   return (     <div>       { }       { }       <Script         src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"         strategy="afterInteractive"       />       <Script id="google-analytics" strategy="afterInteractive">         {`           window.dataLayer = window.dataLayer || [];           function gtag(){dataLayer.push(arguments);}           gtag('js', new Date());           gtag('config', 'GA_MEASUREMENT_ID');         `}       </Script>       { }       <Script         src="/scripts/my-less-important-script.js"         strategy="lazyOnload"       />     </div>   ); }

Le strategie principali sono:

  • beforeInteractive: Per script critici che devono essere eseguiti prima che la pagina diventi interattiva (usare con cautela!).
  • afterInteractive (default): Ideale per la maggior parte degli script (analytics, tag manager). Carica subito dopo l’idratazione della pagina.
  • lazyOnload: Per script a bassa priorità (es. chat widget, pulsanti social). Carica dopo che tutto il resto è stato caricato e il browser è inattivo.

8. Monitorare è Fondamentale

Come fai a sapere se le tue ottimizzazioni funzionano? Devi misurare!

  • Lighthouse: Integrato negli strumenti per sviluppatori di Chrome (scheda “Lighthouse” o “Performance insights”). Analizza la tua pagina e ti dà un punteggio su performance, accessibilità, SEO, etc., con suggerimenti specifici.
  • PageSpeed Insights: Tool online di Google che usa Lighthouse e dati reali degli utenti (CrUX) per darti una visione completa.
  • Vercel Analytics: Se hosti su Vercel, ti offre analytics focalizzati sulle performance (Core Web Vitals) basati sui visitatori reali, senza impattare la privacy.
  • Strumenti di Profiling del Browser: La scheda “Performance” negli strumenti per sviluppatori ti permette di registrare e analizzare nel dettaglio cosa succede durante il caricamento e l’interazione con la pagina (tempi di esecuzione JS, rendering, ecc.).

Consiglio: Non ottimizzare alla cieca! Misura prima, identifica i colli di bottiglia, applica le ottimizzazioni, e poi misura di nuovo per vedere l’impatto.

In Conclusione: Un Viaggio Continuo

Ottimizzare le performance di un sito Next.js non è un’attività da fare una volta e poi dimenticarsene. È più un viaggio continuo. Nuove funzionalità di Next.js escono, le dipendenze si aggiornano, i contenuti del sito cambiano… è importante tenere d’occhio le metriche e continuare a sperimentare.

Ma non farti spaventare! Già applicando i consigli su rendering, immagini, dynamic imports e script di terze parti, vedrai probabilmente dei miglioramenti notevoli. Next.js ci dà strumenti potenti, sta a noi usarli al meglio.

Quindi, cosa aspetti? Prendi uno dei punti che ti sembra più rilevante per il tuo progetto e inizia a sperimentare. Analizza il tuo bundle, prova a cambiare strategia di rendering per una pagina, implementa next/image. Vedrai che la soddisfazione di vedere il tuo sito diventare più scattante ripagherà lo sforzo! Fammi sapere come va, eh? 

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Translate »
Torna in alto