Hirefullstack – Software Engineering & IT-Beratung aus Berlin
← Zur Übersicht
Kern 14 Min Lesezeit

Stolpersteine

Die Fehler, die in fast jedem Next.js-Projekt einmal passieren

In einem Satz

Acht Muster, die immer wieder auftauchen – jeweils mit dem Denkfehler dahinter und der kürzesten Reparatur.

Wenn du die vorigen Lektionen gelesen hast, kennst du die Bausteine. Diese hier sammelt die Fälle, in denen sie im Alltag falsch zusammengesetzt werden.

1. „use client" ganz oben in der Seite

die ganze Seite wandert in den Browser
"use client";

export default function Seite() {
  const [offen, setOffen] = useState(false);
  return (
    <>
      <RiesigeListe />
      <button onClick={() => setOffen(true)}>Filter</button>
    </>
  );
}
nur der interaktive Teil
export default async function Seite() {
  const daten = await laden();
  return (
    <>
      <RiesigeListe daten={daten} />
      <FilterKnopf />          {/* nur diese Datei ist Client */}
    </>
  );
}

Warum es passiert: Die Fehlermeldung zeigt auf die Seite, also setzt man die Zeile dort. Richtig ist, den interaktiven Teil herauszulösen.

2. Die eigene API aus dem eigenen Server aufrufen

So nicht
// in einer Server-Komponente
const res = await fetch("https://meine-seite.de/api/artikel");
const artikel = await res.json();
So geht's
// in einer Server-Komponente
const artikel = await db.artikel.findMany();

Ein Netzwerkumweg zu sich selbst, plus Serialisierung nach JSON und zurück. Route HandlerEine `route.ts`, die auf eine Adresse antwortet, ohne eine Seite zu sein – der Weg zu einer eigenen API. sind für Zugriffe von außen da.

3. Caching-Fragen im Entwicklungsmodus untersuchen

Stolperstein Im Dev-Modus siehst du das Problem nicht

Dort ist fast nichts zwischengespeichert. Wer wissen will, ob eine Seite statisch wird, braucht npm run build && npm run start – und die Symbolspalte der Bauausgabe.

4. params direkt benutzen

seit Next.js 15 falsch
export default function Seite({ params }: { params: { slug: string } }) {
  return <h1>{params.slug}</h1>;
}
richtig
export default async function Seite({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  return <h1>{slug}</h1>;
}

Gilt genauso für searchParams, cookies() und headers(). Ältere Beispiele im Netz zeigen noch den direkten Zugriff.

5. Server Action ohne Berechtigungsprüfung

jeder kann alles löschen
"use server";

export async function loeschen(id: string) {
  await db.artikel.delete({ where: { id } });
}
die Action prüft selbst
"use server";

export async function loeschen(id: string) {
  const nutzer = await aktuellerNutzer();
  if (!nutzer?.istAdmin) throw new Error("Nicht erlaubt");

  await db.artikel.delete({ where: { id } });
}

Für jede Action entsteht ein öffentlicher Endpunkt. Dass der Knopf nur Administratoren angezeigt wird, schützt nichts.

6. Nach dem Schreiben nicht revalidieren

nach-dem-schreiben.ts
"use server";
import { revalidatePath } from "next/cache";

export async function anlegen(formData: FormData) {
  await db.artikel.create({ data: … });

  revalidatePath("/blog");        // die Übersicht
  revalidatePath("/");            // falls dort die neuesten stehen
}
Ohne diese Zeilen zeigt die Übersicht weiter den alten Stand – und man sucht den Fehler in der Datenbank.

7. Alles in einen Suspense-Bereich packen

eine Ladeanzeige für alles
<Suspense fallback={<Spinner />}>
  <Artikel />
  <Kommentare />        {/* nur der hier ist langsam */}
  <Empfehlungen />
</Suspense>
der langsame Teil allein
<Artikel />
<Suspense fallback={<KommentarSkelett />}>
  <Kommentare />
</Suspense>
<Empfehlungen />

Sonst wartet der schnelle Inhalt auf den langsamen – genau das, was Streaming verhindern sollte.

8. useEffect für Daten, die es auf dem Server gibt

React-Gewohnheit
"use client";

function Liste() {
  const [daten, setDaten] = useState([]);
  useEffect(() => {
    fetch("/api/artikel").then(r => r.json()).then(setDaten);
  }, []);

}
Next.js-Weg
export default async function Liste() {
  const daten = await db.artikel.findMany();

}

Der zweite Weg spart die API, den Ladezustand, das Wettrennen – und der Inhalt steht sofort im HTML.

Genauer erklärt: eine Checkliste für die Fehlersuche optional
checkliste.txt
Seite zeigt alte Daten
  1. npm run build – steht dort ○ statt ƒ?
  2. Harter Reload: frisch? → Router Cache im Browser
  3. Nach neuem Build frisch? → revalidate fehlt
  4. Immer noch alt? → Data Cache, revalidateTag prüfen

"needs useState" beim Bauen
  → Hook in einer Server-Komponente.
    Interaktiven Teil herauslösen, nicht die Seite markieren.

Bundle plötzlich viel größer
  → Client-Grenze ist nach oben gerutscht.
    In der Bauausgabe "First Load JS" vergleichen.

Server Action tut nichts
  → Fehlt revalidatePath? Oder wurde geworfen statt
    einen Fehlerwert zurückzugeben?
Tipp Die Bauausgabe ist das beste Werkzeug

Die meisten dieser Fälle sieht man in der Tabelle von npm run build, bevor man auch nur eine Datei öffnet. Ein kurzer Blick darauf bei jeder größeren Änderung erspart lange Suchen.

Sitzt das schon?

6 Fragen zu dieser Lektion. Falsche Antworten landen in deiner Statistik.

Hirefullstack

Ihr braucht React-Verstärkung im Team?

Wir bauen seit Jahren React- und Next.js-Anwendungen für Kunden in ganz Deutschland – als einzelner Experte, als Verstärkung fürs Bestandsteam oder als komplettes Scrum-Team.

Projekt besprechen →