Session lesen mit auth()

Jetzt zahlt sich die Vereinheitlichung aus: In Server Components, Route Handlers und Server Actions schreibst du immer const session = await auth(). Nur der Client tanzt aus der Reihe — dort gibt es weiterhin den reaktiven Hook useSession().

Faustregel: Liest du serverseitig (Server Component, Action, Handler), nimm auth(). Brauchst du den Login-Status reaktiv im Browser (z.B. ein Avatar im Header, der sofort umschaltet), nimm useSession() — und wickle die App in einen SessionProvider.

Server Component — der Normalfall

app/profil/page.tsx
import { auth } from "@/auth";

export default async function Profil() {
  const session = await auth();

  if (!session) return <p>Nicht eingeloggt.</p>;

  return <p>Hallo, {session.user?.name}!</p>;
}

Route Handler — gleiche Funktion

app/api/me/route.ts
import { auth } from "@/auth";

export const GET = auth((req) => {
  if (!req.auth) {
    return Response.json({ error: "Unauthorized" }, { status: 401 });
  }
  return Response.json({ user: req.auth.user });
});

Wickelst du den Handler in auth(...), hängt die Session direkt an req.auth — kein separater Aufruf nötig. Du könntest stattdessen auch hier await auth() rufen.

Client — der reaktive Hook

Der Hook braucht Kontext. Einmal pro App den Provider setzen, dann ist useSession() überall im Client-Tree verfügbar:

app/providers.tsx
"use client";
import { SessionProvider } from "next-auth/react";

export function Providers({ children }: { children: React.ReactNode }) {
  return <SessionProvider>{children}</SessionProvider>;
}
eine Client-Komponente
"use client";
import { useSession } from "next-auth/react";

export function Avatar() {
  const { data: session, status } = useSession();
  if (status === "loading") return <Spinner />;
  return session ? <img src={session.user?.image ?? ""} /> : null;
}

Wer was benutzt

Server: await auth()
Server Components · Route Handlers · Server Actions · Middleware. Synchron-fühlend, läuft vor dem Rendern, kein Provider nötig.
Client: useSession()
Nur in "use client"-Komponenten, braucht SessionProvider, dafür reaktiv: ändert sich der Login-Status, rendert die Komponente neu.
Warum eigentlich? — Warum auth() im Server fast immer reicht
Im App Router rendern Server Components vor der Auslieferung. Du kennst den Login-Status also schon beim Rendern und kannst direkt das richtige UI schicken — ohne Flackern, ohne Lade-Spinner, ohne dass das JWT je den Browser erreicht. useSession() brauchst du nur, wenn sich der Status im laufenden Client ändern soll, ohne die Seite neu zu laden.
Häufiger Denkfehler — auth() im Client aufrufen
auth() ist eine Server-Funktion. Importierst du sie in eine "use client"-Komponente, bekommst du einen Build- oder Runtime-Fehler. Umgekehrt gilt dasselbe: useSession() ist ein React-Hook und funktioniert nur im Client mit Provider. Merksatz: auth() = Server, useSession() = Client.
Tiefer rein — Was im Session-Objekt steckt — und wie du es erweiterst
Standardmäßig enthält session.user nur name, email und image. Willst du z.B. eine Rolle oder die User-ID mitführen, reichst du sie über die jwt- und session-Callbacks durch: im jwt-Callback ins Token schreiben, im session-Callback von dort aufs Session-Objekt heben. Per Module-Augmentation kannst du den Session-Typ dann TypeScript-sauber erweitern.