aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Webentwicklung

Next.js Server Components: Wann welche wählen

Sobald du ein Projekt mit dem Next.js App Router öffnest, bewegst du dich standardmäßig in der Welt der Next.js Server Components. Das alte Modell, das wir gewohnt waren — "jede React-Komponente läuft im Browser" — ist einer zweigeteilten Architektur gewichen, in der manche Komponenten auf dem Server gerendert werden und nur ein Teil an den Browser ausgeliefert wird. In diesem Artikel erkläre ich den wirklichen Unterschied zwischen Server Components und Client Components, wie sie zusammenarbeiten und welche du im Alltag wählst – mit konkreten Beispielen.

Was sind Server Components und Client Components?

Der Unterschied hängt davon ab, wo eine Komponente läuft.

  • Server Component (RSC): läuft nur auf dem Server. Ihre Ausgabe wird als HTML plus eine spezielle serialisierte Nutzlast an den Browser gesendet; das JavaScript der Komponente selbst wird nicht an den Client ausgeliefert. Sie kann direkt auf die Datenbank, das Dateisystem oder geheime API-Schlüssel zugreifen.
  • Client Component: einmal auf dem Server gerendert und dann im Browser "hydriert" (interaktiv gemacht). useState, useEffect, Event-Listener und Browser-APIs leben hier.

Im App Router ist jede Komponente standardmäßig eine Server Component. Um eine Komponente zu einer Client Component zu machen, fügst du die Direktive "use client" am Anfang der Datei hinzu.

Was macht "use client" wirklich?

Ein verbreitetes Missverständnis ist, dass eine mit "use client" markierte Datei "nur im Browser" läuft. Tatsächlich wird diese Komponente beim ersten Request weiterhin auf dem Server gerendert; die Aufgabe der Direktive ist es, die Client-Grenze (client boundary) zu markieren, die in dieser Datei beginnt. Ab dieser Grenze werden die Komponente und das, was sie importiert, in das Client-Bundle aufgenommen.

"use client";

import { useState } from "react";

export default function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>
      Zähler: {count}
    </button>
  );
}

Die Direktive muss ganz oben in der Datei stehen, vor jedem Import. Sobald du eine Client-Component-Grenze überschreitest, werden auch alle darin gerenderten Kindkomponenten auf dem Client ausgewertet.

Welche sollte man wählen – und wann?

Die praktische Regel ist einfach: Belasse den Standard als Server Component und wechsle nur dann zu Client, wenn du Interaktivität oder eine Browser-API brauchst.

Bevorzuge eine Server Component, wenn:

  • du Daten aus einer Datenbank oder einer API abrufst (direkt mit async/await).
  • du Schlüssel, Tokens oder Connection-Strings verwendest, die geheim bleiben müssen.
  • du eine schwere Abhängigkeit (etwa einen Markdown-Parser oder eine Datumsbibliothek) aus dem Client-Bundle heraushalten willst.
  • der Inhalt weitgehend statisch ist und keine Interaktion erfordert.

Bevorzuge eine Client Component, wenn:

  • du State (useState, useReducer) oder Lifecycle (useEffect) brauchst.
  • du Event-Listener wie onClick oder onChange verwendest.
  • du browserspezifische APIs wie window, localStorage oder IntersectionObserver benötigst.
  • du auf eine Client-only-Bibliothek angewiesen bist (etwa viele Animations- oder Kartenkomponenten).

Beide zusammen nutzen: das Kompositionsmuster

Die eigentliche Stärke liegt im Verschachteln der beiden. Das gängige, empfohlene Muster ist, Daten auf dem Server abzurufen und sie einer kleinen Client Component als Prop zu übergeben. So hältst du den interaktiven Teil klein und lässt die schwere Arbeit auf dem Server.

// app/page.tsx  (Server Component)
import Counter from "./counter";

export default async function Page() {
  const data = await getData(); // läuft auf dem Server
  return (
    <main>
      <h1>{data.title}</h1>
      <Counter />        {/* Client-Grenze */}
    </main>
  );
}

Ein wichtiges Detail: Du kannst eine Server Component nicht innerhalb einer Client Component importieren und rendern, aber du kannst sie als children übergeben. So kannst du Inhalte einer Server Component innerhalb eines Client-Wrappers (etwa eines Theme-Providers) anzeigen:

// Client Component
"use client";
export default function Panel({ children }) {
  return <div className="panel">{children}</div>;
}

// Server Component
<Panel>
  <ServerContent />   {/* als children übergeben, bleibt auf dem Server */}
</Panel>

Häufige Fehler

  • Überall "use client" setzen. Das wirft alle Vorteile von RSC weg; das Bundle wächst und der erste Ladevorgang wird langsamer. Schiebe die Direktive so nah wie möglich an die Blatt-Komponente (die innerste interaktive Komponente).
  • Versuchen, useState oder einen Event-Handler in einer Server Component zu verwenden. Das erzeugt einen Build-/Laufzeitfehler; du musst die Komponente zu einer Client Component machen.
  • Einen geheimen Schlüssel in eine Client Component verschieben. Alles, was im Client-Bundle landet, ist für jeden sichtbar. Geheimnisse müssen ausschließlich in Server Components oder serverseitigem Code bleiben.
  • Eine async Client Component schreiben. Server Components dürfen async sein, um Daten abzurufen; um Daten in Client Components zu laden, nutze useEffect oder eine Bibliothek wie SWR.

Häufige Fragen

Haben Server Components getServerSideProps ersetzt?

Größtenteils ja. Der App Router hat kein getServerSideProps oder getStaticProps mehr; du rufst Daten direkt mit await innerhalb einer async Server Component ab. Das Caching- und Revalidierungsverhalten steuerst du anschließend über die Optionen von fetch.

Kann eine Client Component eine Server Component als Kind aufnehmen?

Ja. Eine Client Component kann eine Server Component nicht direkt importieren und rendern, aber sie kann eine über children oder eine andere Prop entgegennehmen. Das ist der Standardweg, um interaktive Wrapper mit serverseitig gerendertem Inhalt zu kombinieren.

Warum funktioniert useState in einer Server Component nicht?

Weil useState State benötigt, der im Browser lebt und über Renders hinweg erhalten bleibt; eine Server Component hat keinen clientseitigen Lifecycle. Wenn du State oder Interaktivität brauchst, lagere diesen Teil in eine Client Component aus.

Möchtest du deine Next.js-Architektur richtig aufbauen? Ich helfe dir, eine schnelle, wartbare App zu bauen, die Server und Client Components gut ausbalanciert. Nimm Kontakt auf.

Bu kategorideki tüm yazılar →

Devamı için