Alle artikelen
Next.js Server Components in Productie: 5 Lessen Die Ik Op de Harde Manier Leerde
OntwikkelingNext.jsReactServer ComponentsPerformanceTypeScriptWeb Development

Next.js Server Components in Productie: 5 Lessen Die Ik Op de Harde Manier Leerde

Server Components beloven kleinere bundles en betere performance. Maar de weg naar productie zit vol valkuilen. Dit zijn de vijf lessen die ik leerde bij het bouwen van echte applicaties.

NA
Naoufal Andichi
4 min. leestijd14 maart 20261 weergave

Server Components zijn niet nieuw meer. Maar het verschil tussen ermee experimenteren en ze daadwerkelijk in productie draaien is groter dan de documentatie doet vermoeden.

Bij het bouwen van applicaties met Next.js 15 en de App Router ben ik tegen genoeg muren aangelopen om er iets nuttigs over te schrijven. Dit zijn de vijf lessen die het verschil maakten.

Les 1: Default naar Server, maar weet waarom

De vuistregel is simpel: alles is een Server Component tenzij het interactief moet zijn. Maar "interactief" is breder dan je denkt.

Deze dingen vereisen "use client":

  • useState, useEffect, en andere React hooks
  • onClick handlers en form submissions
  • Browser API's (localStorage, window, etc.)
  • Animatiebibliotheken als Framer Motion
  • Context providers

Deze dingen horen bij Server Components:

  • Data fetching
  • Database queries
  • Backend API calls
  • Zware berekeningen
  • Gevoelige operaties met API keys

De fout die ik aanvankelijk maakte: hele pagina's als client components markeren omdat een knop interactief moest zijn. De oplossing is om de client boundary zo ver mogelijk naar beneden te duwen. Maak alleen het interactieve deel een client component, en laat de rest op de server.

Het concrete resultaat bij een project: de initiele JavaScript bundle ging van 247KB naar 156KB, een daling van 37%.

Les 2: Streaming en Suspense zijn geen optionele features

Lange tijd behandelde ik Suspense als een nice-to-have. Dat was een vergissing.

Zonder Suspense wacht je gebruiker tot de langzaamste query klaar is voordat de pagina verschijnt. Met Suspense stream je componenten zodra ze klaar zijn. De gebruiker ziet content verschijnen terwijl zwaardere onderdelen nog laden.

In de praktijk betekent dit: wrap elke async Server Component die data ophaalt in een Suspense boundary met een loading skeleton. Het voelt sneller, zelfs als de totale laadtijd gelijk blijft.

import { Suspense } from "react";
import { ProjectGrid } from "@/components/projects/project-grid";
import { ProjectGridSkeleton } from "@/components/skeletons";

export default function ProjectsPage() {
  return (
    <main>
      <h1>Projecten</h1>
      <Suspense fallback={<ProjectGridSkeleton />}>
        <ProjectGrid />
      </Suspense>
    </main>
  );
}

Les 3: De caching-strategie bepaalt alles

Next.js 15 biedt drie renderstrategieen, en de keuze heeft directe impact op performance:

  • Statisch: Gebouwd bij deploy, geserveerd vanaf CDN. Maximale snelheid.
  • Revalidating: Statisch, maar ververst periodiek. Voor content die af en toe verandert.
  • Dynamisch: Gerenderd per request. Voor gepersonaliseerde of realtime content.

Mijn aanpak: begin statisch en maak alleen dynamisch wat dynamisch moet zijn. Blogposts? Statisch met revalidatie elke 3600 seconden. Dashboard? Dynamisch. Projectpagina? Statisch met on-demand revalidatie bij updates vanuit het admin panel.

De valkuil: een enkele cookies() of headers() aanroep in je component maakt de hele route dynamisch. Wees hier bewust mee.

Les 4: Data fetching patronen maken of breken je app

In de App Router fetch je data direct in Server Components. Geen getServerSideProps meer, geen useEffect-waterval. Maar er zijn patronen die het verschil maken:

Parallel data fetching: Gebruik Promise.all wanneer queries onafhankelijk van elkaar zijn.

async function DashboardPage() {
  const [projects, posts, stats] = await Promise.all([
    getProjects(),
    getRecentPosts(),
    getSiteStats(),
  ]);
  
  return <Dashboard projects={projects} posts={posts} stats={stats} />;
}

Vermijd watervallen: Als Component B data nodig heeft van Component A, en beide op dezelfde pagina staan, gebruik dan parallelle fetching op paginaniveau in plaats van geneste awaits.

Supabase server client: Maak een server-side Supabase client aan per request. Deel geen clients tussen componenten.

Les 5: Core Web Vitals als meetlat

Al het bovenstaande is theorie zonder meetbare resultaten. De cijfers die ik als target gebruik:

  • LCP (Largest Contentful Paint): Onder 2.5 seconden
  • INP (Interaction to Next Paint): Onder 200ms
  • CLS (Cumulative Layout Shift): Onder 0.1

Na het toepassen van deze lessen op een project met Supabase als backend ging de LCP van 2.8s naar 1.6s. CLS daalde van 0.25 naar 0.05. De JavaScript bundle werd 37% kleiner.

Dat zijn geen magische cijfers. Het zijn directe resultaten van bewuste architectuurkeuzes.

De kern

Server Components zijn geen silver bullet. Ze zijn een tool die, mits correct toegepast, je applicatie sneller, lichter en beter onderhoudbaar maakt. Maar het vereist discipline: de juiste rendering-strategie kiezen, Suspense boundaries plaatsen, en constant meten.

De documentatie vertelt je het "wat." De productie leert je het "hoe."

NA

Geschreven door

Naoufal Andichi

Full Stack Developer uit Ede. Bouwt webapplicaties met PHP, Laravel, React en Next.js.

DelenDelen op XLinkedIn

Terug naar alle artikelen