# React 404 Seite: 5 Fehlerseiten für den Next.js App Router

> React 404 Seite, die Besuchern weiterhilft, plus Screens für 500, Wartung, Offline und fehlenden Zugriff, sauber im Next.js App Router mit echtem Statuscode.

- Autor: [Serban Rusu](https://wingo-ui.com/blog/authors/serban), Founder of Wingo UI
- Veröffentlicht: 16. Okt. 2026
- Kategorie: [Praxis: Screens und Apps](https://wingo-ui.com/de/blog/category/build)
- Lesezeit: 12 Min.
- Kanonische URL: https://wingo-ui.com/de/blog/react-404-page
- Englische Fassung: https://wingo-ui.com/blog/react-404-page

## Kurz gesagt

Eine gute React 404 Seite sagt, was schiefging, bietet eine Suche und ein paar naheliegende Ziele an und antwortet mit einem echten 404-Status (oder zumindest noindex), damit Suchmaschinen die URL verwerfen. Im Next.js App Router übernimmt not-found.tsx den 404, error.tsx und global-error.tsx den 500, forbidden.tsx den 403, proxy.ts liefert die Wartungsseite mit einem 503 aus und ein Service Worker die Offline-Seite. Der Error Pages Block von Wingo UI (Pro) rendert alle fünf Screens über einen einzigen variant-Prop.

Die meisten Vorlagen für eine React 404 Seite bestehen aus einer großen Zahl, einer traurigen Illustration und einem Button „Go home“. Damit ist einer der fünf Fehler-Screens abgedeckt, auf die eine echte App trifft, und den Statuscode überlässt sie dem, was der Server gerade schickt. Dieses Tutorial baut eine React 404 Seite, die Leuten hilft, ans Ziel zu kommen, und danach die vier Screens daneben: eine 500-Seite mit Retry, eine Wartungsseite mit Countdown, eine Offline-Seite für deine PWA und eine Seite für fehlenden Zugriff, über die man Zugriff anfragen kann. Alles wird im Next.js App Router verdrahtet, mit den Dateikonventionen von Next.js 16.3. Wir bauen Wingo UI, und der Code nutzt unseren Block [Error Pages](https://wingo-ui.com/components/error-pages). Wir sind also nicht neutral; die Verdrahtung funktioniert mit jeder Komponente.

## Was sollte eine React 404 Seite leisten?

Sie bringt die Person auf eine funktionierende Seite und hält die tote URL aus der Suche heraus. Vier Dinge entscheiden, ob jemand den Weg zurückfindet:

- **Sag in klaren Worten, was passiert ist.** „Diese Seite gibt es nicht oder sie wurde verschoben“, nicht „Error 404“.
- **Biete den nächsten Schritt an.** Ein Suchfeld, die drei oder vier Seiten, die Leute am häufigsten suchen, und einen Weg zurück dorthin, wo sie herkamen.
- **Liefere den richtigen Status.** Google markiert eine Seite, die mit 200 antwortet, aber eine Fehlermeldung zeigt, in der Search Console als [Soft 404](https://developers.google.com/search/docs/crawling-indexing/http-network-errors). Die Not-found-Oberfläche braucht einen 404, oder zumindest ein `noindex`.
- **Setz den Fokus nach einer clientseitigen Navigation auf den Titel**, damit jemand mit Screenreader hört, dass sich die Seite geändert hat. Beim ersten Laden lässt du das, denn da beginnt der Browser ohnehin oben. Der Error Pages Block hält sich über seinen Prop `autoFocusTitle` an diese Regel (standardmäßig an).

Dieselbe Checkliste gilt für die anderen vier Fehler-Screens, jeweils mit einem anderen nächsten Schritt.

## Welche Fehlerseiten braucht eine Next.js App?

Fünf: nicht gefunden, Serverfehler, Wartung, offline und kein Zugriff. Jede hat ihren eigenen Einstiegspunkt im App Router:

| Screen | Einstiegspunkt im App Router | Status | Was die Person tun kann |
|---|---|---|---|
| Nicht gefunden | `app/not-found.tsx`, `notFound()` | 404 (200 + noindex beim Streaming) | Suchen, eine vorgeschlagene Seite öffnen, zurückgehen |
| Serverfehler | `app/error.tsx`, `app/global-error.tsx` | 500 | Erneut versuchen, eine Fehler-ID für den Support kopieren |
| Wartung | `proxy.ts` leitet den Traffic auf `/maintenance` | 503 + `Retry-After` | Sehen, wann sie endet, die Statusseite prüfen |
| Offline | Ein Fallback im Service Worker, `useOffline` | keiner (kein Netz) | Auf den automatischen Retry warten, gespeicherte Seiten öffnen |
| Kein Zugriff | `app/forbidden.tsx`, `forbidden()` | 403 | Zugriff anfragen, Konto wechseln |

Die Fakten in dieser Tabelle stammen aus der Doku von Next.js 16.3, die in `node_modules/next/dist/docs` mitkommt, gelesen am 16. Oktober 2026. `forbidden.tsx` und `useOffline` sind dort noch experimentell.

## Wie installiere ich eine Vorlage für eine React 404 Seite?

Installier den Error Pages Block mit der CLI:

```bash
npx wingo-ui@latest add error-pages
```

Das kopiert `components/blocks/error-pages.tsx` mit seiner Datendatei und den Teilen, aus denen er besteht (Alert, Countdown, Tracker, Dialog, Suchfeld und ein paar mehr), dazu die npm-Pakete, die sie brauchen, darunter `motion`, `lucide-react`, `react-hook-form` und `zod`. Error Pages gehört zu [Wingo UI Pro](https://wingo-ui.com/de/pricing).

Die Standardwerte des Blocks sind Demo-Inhalte: ein Acme-CRM-Logo, `support@acme.com`, Beispiel-Admins, ein Beispiel-Stacktrace. Gib einmal deine eigenen mit, in einem kleinen Client-Wrapper, der ihm auch den `Link` von Next.js übergibt:

```tsx
// components/app-error-page.tsx
"use client"

import Link from "next/link"
import { ErrorPage, type ErrorPageProps } from "@/components/blocks/error-pages"

export function AppErrorPage(props: ErrorPageProps) {
  return (
    <ErrorPage
      linkComponent={Link}
      logo={<span className="font-semibold">Northwind</span>}
      supportHref="mailto:help@northwind.app"
      statusHref=""
      {...props}
    />
  )
}
```

`statusHref=""` blendet die Links zur Statusseite aus, bis du eine hast. Server-Dateien wie `not-found.tsx` können diesen Wrapper direkt rendern, solange jeder Prop, den sie übergeben, reine Daten, JSX (Icons gehen) oder eine Server Action ist. Funktionen, die in einer Server-Datei definiert sind, lassen sich nicht an den Client-Wrapper übergeben.

## Wie baue ich eine Next.js 404 Seite?

Leg `app/not-found.tsx` an. Diese Datei ist die Next.js not found page: Sie rendert für jede URL, zu der keine Route passt, und für jedes Segment, das `notFound()` aufruft:

```tsx
// app/not-found.tsx
import { BarChart3, FileText, LayoutDashboard, LifeBuoy } from "lucide-react"
import { AppErrorPage } from "@/components/app-error-page"

const suggestions = [
  { label: "Dashboard", href: "/dashboard", icon: <LayoutDashboard />, description: "Der Tag auf einen Blick" },
  { label: "Rechnungen", href: "/invoices", icon: <FileText />, description: "Gesendet, bezahlt und überfällig" },
  { label: "Berichte", href: "/reports", icon: <BarChart3 />, description: "Umsatz nach Monat" },
  { label: "Hilfe-Center", href: "/help", icon: <LifeBuoy />, description: "Anleitungen und Antworten" },
]

export default function NotFound() {
  return <AppErrorPage variant="404" suggestions={suggestions} />
}
```

Das Suchfeld navigiert standardmäßig mit Enter zu `/search?q=`. Übergib `onSearch` aus einer Client-Komponente, um woandershin zu leiten, oder `showSearch={false}`, wenn du keine Suche hast. „Go back“ nutzt `history.back()`, wenn es einen Verlauf gibt, und fällt auf den Link zur Startseite zurück, wenn der Tab keinen Verlauf hat, etwa weil der Link in einem neuen Tab geöffnet wurde.

> Live-Demo (Error Pages): Drück /, um die Suche zu fokussieren, tipp eine Anfrage und drück Enter, oder fahr über die Vorschlagskarten: Jede ist ein einfacher Link. Probier sie unter [Error Pages](https://wingo-ui.com/components/error-pages) aus und installier sie mit `npx wingo-ui@latest add error-pages`.

Für einen fehlenden Datensatz rufst du `notFound()` in der Seite auf, die ihn lädt:

```tsx
// app/invoices/[id]/page.tsx
import { notFound } from "next/navigation"
import { getInvoice } from "@/lib/invoices"

export default async function InvoicePage({ params }: { params: Promise<{ id: string }> }) {
  const { id } = await params
  const invoice = await getInvoice(id)
  if (!invoice) notFound()
  return <InvoiceView invoice={invoice} />
}
```

Zwei Details zum Status, die die Doku ausdrücklich nennt. `notFound()` liefert einen 404, solange die Antwort noch nicht gestreamt wird. Hat eine `loading.tsx`-Boundary die Shell schon gesendet, bleibt der Status 200, und Next.js fügt stattdessen `<meta name="robots" content="noindex">` ein. Brauchst du den echten 404 für deine Analytics, prüf, ob der Datensatz existiert, bevor irgendetwas gestreamt wird. Und wenn eine App mehrere Root-Layouts hat, gibt dir das experimentelle `global-not-found.tsx` (hinter `experimental.globalNotFound`) einen einzigen 404, der ganz ohne Layout rendert.

### Wann ist eine komplette 404-Seite zu viel?

Wenn die URL stimmt und nur die Daten leer sind. Eine Suche ohne Treffer, eine gefilterte Tabelle mit null Zeilen oder ein neues Konto ohne Rechnungen sollten mit 200 und einem Empty State innerhalb der Seite antworten, nie mit einem 404. Der kostenlose [Empty State](https://wingo-ui.com/components/empty-state) deckt diesen Fall mit seinen Presets `no-results`, `empty`, `no-permission` und `offline` ab, und `live` kündigt ihn Screenreadern an, wenn sich die Ergebnisse ändern:

```tsx
// app/invoices/invoice-list-empty.tsx
"use client"

import { Button } from "@/components/ui/button"
import { EmptyState } from "@/components/ui/empty-state"

export function InvoiceListEmpty({ query, clearFilters }: { query: string; clearFilters: () => void }) {
  return (
    <EmptyState
      preset="no-results"
      live
      title={`Keine Rechnungen zu "${query}"`}
      description="Versuch einen anderen Kundennamen oder setz die Filter zurück."
      actions={<Button variant="outline" onClick={clearFilters}>Filter zurücksetzen</Button>}
    />
  )
}
```

## Wie zeige ich eine 500-Seite mit Retry?

Leg `app/error.tsx` an und verbinde seinen Prop `retry` mit dem Button „Try again“. Die Datei packt jedes Segment in eine React Error Boundary, also muss sie eine Client-Komponente sein. Seit Next.js 16.3 ist der Prop `retry` stabil. Er lädt die Daten des Segments neu und rendert es neu, anders als das ältere `reset()`, das nur neu rendert, ohne neu zu laden. Ein Segment, dessen Datenabruf fehlschlug, zeigt mit `reset()` also wieder denselben Fehler:

```tsx
// app/error.tsx
"use client"

import { usePathname } from "next/navigation"
import { useEffect } from "react"
import { AppErrorPage } from "@/components/app-error-page"

export default function Error({ error, retry }: { error: Error & { digest?: string }; retry: () => void }) {
  const pathname = usePathname()

  useEffect(() => {
    console.error(error)
  }, [error])

  return (
    <AppErrorPage
      variant="500"
      errorId={error.digest ?? ""}
      error={error}
      errorPath={pathname}
      showDetails={process.env.NODE_ENV === "development"}
      onRetry={retry}
    />
  )
}
```

`error.digest` ist der Hash, den Next.js auch ins Server-Log schreibt. Damit eignet er sich gut als ID, die Leute dem Support nennen können: Der Block kopiert ihn mit einem Klick. Fehler, die im Browser geworfen werden, haben keinen Digest, und `errorId=""` blendet die Zeile mit der ID aus. In Produktion kommen Fehler aus Server Components mit einer generischen Meldung beim Client an, und den Stacktrace brauchen Besucher ohnehin nicht. Deshalb erscheinen die technischen Details hier nur in der Entwicklungsumgebung.

> Live-Demo (Error Pages): Drück Try again: Der erste Versuch scheitert und zeigt, wann, der zweite klappt. Öffne Technical details, um Pfad und Stacktrace zu sehen. Probier sie unter [Error Pages](https://wingo-ui.com/components/error-pages) aus und installier sie mit `npx wingo-ui@latest add error-pages`.

`error.tsx` fängt keine Fehler im Root-Layout ab. Dafür ergänzt du `app/global-error.tsx`. Diese Datei ersetzt das ganze Dokument, rendert also ihr eigenes `<html>` und `<body>` und importiert deine Styles:

```tsx
// app/global-error.tsx
"use client"

import "./globals.css"
import { AppErrorPage } from "@/components/app-error-page"

export default function GlobalError({ error, retry }: { error: Error & { digest?: string }; retry: () => void }) {
  return (
    <html lang="de">
      <body className="bg-background text-foreground">
        <AppErrorPage variant="500" errorId={error.digest ?? ""} showDetails={false} onRetry={retry} />
      </body>
    </html>
  )
}
```

Sie liegt außerdem außerhalb jedes Theme-Providers. Ein klassenbasierter Dark Mode erreicht sie also nicht: Diese Seite rendert mit deinen hellen Tokens, solange du die Klasse nicht selbst setzt.

Innerhalb einer App-Shell übergibst du `layout="inline"` an eine beliebige Variante, zum Beispiel aus einem verschachtelten `app/(app)/error.tsx`. Die Seite lässt dann ihr eigenes Logo und ihren Footer weg und füllt den Hauptbereich der Shell. Scheitert ein Bericht, bleibt die Sidebar deiner [Admin Shell](https://wingo-ui.com/components/admin-shell) also drumherum stehen.

## Wie baue ich eine React Wartungsseite?

Liefer sie mit einem 503-Status aus und sag den Leuten, wann sie endet. Eine typische React Wartungsseite ist ein statisches „Wir sind bald zurück“, das mit 200 antwortet, und macht damit beides falsch. Googles Doku zu [HTTP-Statuscodes](https://developers.google.com/search/docs/crawling-indexing/http-network-errors) (aktualisiert am 4. Februar 2026) beschreibt, dass 5xx-Antworten die Crawler bremsen, dass Inhalte von diesen URLs ignoriert werden und dass indexierte URLs zunächst erhalten bleiben und erst verschwinden, wenn die Fehler anhalten. Eine Wartungsseite mit 200 bekommt keinen dieser Schutzmechanismen.

Die Seite selbst ist eine ganz normale Route:

```tsx
// app/maintenance/page.tsx
import { connection } from "next/server"
import { AppErrorPage } from "@/components/app-error-page"

export default async function Maintenance() {
  // die env zur Anfragezeit lesen, nicht beim Build
  await connection()

  return (
    <AppErrorPage
      variant="maintenance"
      // ein ISO-Datum wie 2026-10-18T04:00:00Z
      until={process.env.MAINTENANCE_UNTIL}
      showStatus={false}
      showSubscribe={false}
    />
  )
}
```

`until` steuert einen live laufenden [Countdown](https://wingo-ui.com/components/countdown). Erreicht er null, meldet die Seite, dass die Wartung vorbei ist, und der Haupt-Button wird zu „Reload“. Dabei gibt es zwei Fallstricke. Ohne `connection()` wird die Seite vorgerendert und behält den Wert, den `MAINTENANCE_UNTIL` beim Build hatte. Und fehlt die Variable, fällt `until` auf das Beispieldatum des Blocks zurück, das in der Vergangenheit liegt. Die Seite öffnet dann mit „Maintenance is over“. `showStatus` und `showSubscribe` sind aus, weil ihre Standardwerte Beispieldaten sind: Schalte sie ein, sobald du deine eigene `statusHistory` (ein [Tracker](https://wingo-ui.com/components/tracker) über 30 Tage) und eine Server Action für `onSubscribe` übergibst. Für eine vollständige öffentliche Uptime-Seite ist die [Vorlage Status Page](https://wingo-ui.com/templates/status-page) der separate Screen, auf den `statusHref` zeigen sollte.

Um jede Anfrage dorthin zu schicken, nimm `proxy.ts` (die Datei, die in Next.js 16 `middleware.ts` ersetzt hat). Verlass dich für den Status nicht auf `NextResponse.rewrite()`: Im Router von 16.3 reicht ein Rewrite die Header des Proxys weiter, aber die Zielseite rendert die Antwort, und eine normale Seite antwortet mit 200. Für einen echten 503 holst du die gerenderte Seite per fetch und gibst sie mit deinem eigenen Status zurück:

```ts
// proxy.ts
import type { NextRequest } from "next/server"

export async function proxy(request: NextRequest) {
  if (process.env.MAINTENANCE !== "on") return

  const page = await fetch(new URL("/maintenance", request.url))
  return new Response(page.body, {
    status: 503,
    headers: { "Content-Type": "text/html; charset=utf-8", "Retry-After": "3600" },
  })
}

export const config = {
  // die Wartungsseite selbst, ihre Assets und robots.txt bleiben erreichbar
  matcher: ["/((?!maintenance|_next/static|_next/image|favicon.ico|robots.txt).*)"],
}
```

Halt `/maintenance` aus dem Matcher heraus, sonst ruft der fetch wieder den Proxy auf. `Retry-After` nimmt Sekunden oder ein HTTP-Datum; setz es auf das Ende des Wartungsfensters. Clientseitige Navigationen bekommen dasselbe HTML mit einem 503, also weicht der Router auf ein vollständiges Neuladen der Seite aus, und genau das willst du während der Wartung.

## Wie bekommt meine PWA eine Offline-Seite?

Leg eine `/offline`-Route in einem Service Worker in den Cache und liefere sie aus, wenn das Laden einer Seite fehlschlägt. Das deckt vollständige Seitenaufrufe ab; Anfragen in einer App, die schon offen ist, brauchen eine andere Lösung.

**Innerhalb der App** scheitert eine Soft Navigation, ein Prefetch oder eine Server Action, weil das Netz weg ist. Next.js 16.3 hat dafür eine experimentelle Antwort: Mit `experimental.useOffline` werden diese Anfragen wiederholt, sobald die Verbindung zurück ist, und der Hook `useOffline()` aus `next/offline` liefert `true`, damit eine Komponente ein Banner zeigen kann. Ohne das Flag liefert der Hook immer `false`. Eine Fehlerseite braucht es dafür nicht.

**Ein vollständiger Seitenaufruf ohne Netz.** Der Browser hat kein HTML, das er zeigen könnte, also hilft nur ein Service Worker. Leg bei seiner Installation eine Offline-Route in den Cache und liefere sie aus, wenn eine Navigation scheitert:

```js
// public/sw.js
const CACHE = "offline-v1"

self.addEventListener("install", (event) => {
  event.waitUntil(caches.open(CACHE).then((cache) => cache.add("/offline")))
  self.skipWaiting()
})

self.addEventListener("activate", (event) => event.waitUntil(self.clients.claim()))

self.addEventListener("fetch", (event) => {
  if (event.request.mode !== "navigate") return
  event.respondWith(fetch(event.request).catch(() => caches.match("/offline")))
})
```

Registrier ihn einmal aus einer Client-Komponente im Root-Layout:

```tsx
// app/register-sw.tsx
"use client"

import { useEffect } from "react"

export function RegisterServiceWorker() {
  useEffect(() => {
    if ("serviceWorker" in navigator) navigator.serviceWorker.register("/sw.js")
  }, [])
  return null
}
```

Die Offline-Route prüft die Verbindung nach ihrem eigenen Zeitplan. Die `HEAD`-Anfrage ist keine Navigation, also lässt der Service Worker sie zum Netz durch:

```tsx
// app/offline/offline-view.tsx
"use client"

import { AppErrorPage } from "@/components/app-error-page"

async function ping() {
  const res = await fetch(window.location.href, { method: "HEAD", cache: "no-store" })
  if (!res.ok) throw new Error("Immer noch offline")
}

export function OfflineView() {
  return <AppErrorPage variant="offline" retryInterval={10} onRetry={ping} offlinePages={[]} />
}
```

Render `<OfflineView />` aus `app/offline/page.tsx`. Das ist die PWA Offline Seite, die Leute nach einem Neuladen ohne Empfang sehen, weiterhin unter der URL, die sie aufgerufen haben. Sie prüft alle 10 Sekunden erneut und zeigt dabei einen Fortschrittsbalken. Löst der Browser `online` aus, prüft sie sofort, und sobald eine Prüfung klappt, lädt sie die ursprüngliche URL neu. `offlinePages={[]}` blendet die Liste „saved on this device“ aus, bis du wirklich Seiten cachst.

Einen Haken hat der minimale Worker oben: Er cacht nur das HTML von `/offline`. Die CSS- und JavaScript-Chunks unter `/_next/static` liegen nicht im Cache, also erscheint die Seite ohne Netz ungestylt, und die Retry-Schleife startet nie. Leg diese Dateien ebenfalls vorab in den Cache. [Serwist](https://github.com/serwist/serwist), die Option, auf die der PWA-Guide von Next.js verweist, baut diese Precache-Liste aus deinem Build-Output. Wenn du die App zu einer PWA machst, kümmert sich unser Beitrag zum [React PWA Install Prompt](https://wingo-ui.com/de/blog/react-pwa-install-prompt) um die Installation.

## Wie zeige ich eine Seite für fehlenden Zugriff an?

Liefer einen 403, der sagt, wer Zugriff gewähren kann. Für eine angemeldete Person ohne die passende Rolle verschleiert ein 404, dass es die Seite gibt, und eine Weiterleitung zur Anmeldung endet in einer Schleife. Schalte `experimental.authInterrupts` in `next.config.ts` ein, ruf `forbidden()` aus `next/navigation` dort auf, wo du die Rolle prüfst (Next.js ergänzt bei dieser Antwort ein `noindex`-Tag), und leg `app/forbidden.tsx` an. Der Status funktioniert wie bei `notFound()`: Einen echten 403 bekommst du nur, wenn die Prüfung läuft, bevor das Streaming der Antwort beginnt.

```tsx
// app/forbidden.tsx
import { AppErrorPage } from "@/components/app-error-page"
import { getSession, getWorkspaceAdmins } from "@/lib/auth"
import { requestAccess, switchAccount } from "./access-actions"

export default async function Forbidden() {
  const session = await getSession()
  const admins = await getWorkspaceAdmins()

  return (
    <AppErrorPage
      variant="forbidden"
      resource="Finanzberichte"
      admins={admins}
      account={session ? { name: session.user.name, email: session.user.email } : null}
      onRequestAccess={requestAccess}
      onSwitchAccount={switchAccount}
    />
  )
}
```

`getSession`, `getWorkspaceAdmins` und die beiden Server Actions schreibst du selbst. `requestAccess` bekommt `{ adminId, reason }`. Ein abgelehntes Promise zeigt seine Meldung im Dialog, der sich auf Smartphones als Bottom Sheet öffnet. Ein Fallstrick in Produktion: Next.js ersetzt die Meldung eines Fehlers, der in einer Server Action geworfen wird, durch eine generische. „Du hast schon eine offene Anfrage“ würde den Dialog also nicht erreichen. Lass die Action stattdessen `{ error }` zurückgeben und übergib `onRequestAccess` aus einer kleinen Client-Komponente, die sie aufruft und `new Error(result.error)` wirft. Nach einer erfolgreichen Anfrage wird der Button zu einem deaktivierten „Request sent“ mit der Uhrzeit.

> Live-Demo (Error Pages): Drück Request access und dann Send request, ohne etwas auszufüllen, um beide Fehler zu sehen. Wähl einen Admin, schreib einen Grund und schick die Anfrage erneut. Probier sie unter [Error Pages](https://wingo-ui.com/components/error-pages) aus und installier sie mit `npx wingo-ui@latest add error-pages`.

## Wie sieht das fertige Setup aus?

Sechs Routendateien, ein Proxy, ein Service Worker und ein Wrapper um einen Block:

```text
app/
  not-found.tsx        404 für unbekannte URLs und notFound()
  error.tsx            500 pro Segment, retry()
  global-error.tsx     500, wenn das Root-Layout scheitert
  forbidden.tsx        403 aus forbidden()
  maintenance/page.tsx von proxy.ts mit einem 503 ausgeliefert
  offline/page.tsx     von public/sw.js gecacht
  register-sw.tsx      registriert den Service Worker
components/
  app-error-page.tsx   dein Logo, deine Support-Adresse und Link
proxy.ts               503, solange MAINTENANCE=on
public/sw.js           Offline-Fallback für Seitenaufrufe
```

Die passende Shell für die Inline-Varianten baut unser [Tutorial zum Next.js Admin Dashboard](https://wingo-ui.com/de/blog/nextjs-admin-dashboard-tutorial), und weitere komplette Screens findest du in der Kategorie [Praxis: Screens und Apps](https://wingo-ui.com/de/blog/category/build).

Fang mit dem kostenlosen [Empty State](https://wingo-ui.com/components/empty-state) für Sackgassen innerhalb einer Seite an, und nimm den Block [Error Pages](https://wingo-ui.com/components/error-pages) dazu, wenn du alle fünf vollständigen Screens willst: `npx wingo-ui@latest add error-pages`.

## Komponenten in diesem Artikel

- [Error Pages](https://wingo-ui.com/components/error-pages) (Pro): React error pages in one block: 404 not found, 500 server error, maintenance, offline and no permission, full page or inside an app shell. Installation: `npx wingo-ui@latest add error-pages`
- [Empty State](https://wingo-ui.com/components/empty-state) (Free): A React empty state for lists, tables, searches and pages with nothing to show yet: an icon tile, a title, a description and actions. Installation: `npx wingo-ui@latest add empty-state`

## Häufige Fragen

### Wie erstelle ich eine 404-Seite im Next.js App Router?

Leg app/not-found.tsx an. Die Datei rendert für jede URL ohne passende Route und immer dann, wenn eine Server Component notFound() aus next/navigation aufruft, und Next.js ergänzt automatisch ein noindex-Meta-Tag.

### Liefert die Next.js not found page einen 404-Statuscode?

Sie liefert 404, wenn die Antwort nicht gestreamt wird. Hat eine loading.tsx-Boundary das Streaming schon gestartet, bevor notFound() lief, bleibt der Status 200, aber Next.js fügt ein noindex-Meta-Tag ein, damit die URL trotzdem nicht im Index landet.

### Wie baue ich eine 404-Seite mit React Router?

Leg eine Catch-all-Route mit path="*" an, die deine Not-found-Komponente rendert. In einer Single Page App antwortet der Server trotzdem für jeden Pfad mit 200, also stell den Host so ein, dass er für Pfade außerhalb deiner Routenliste 404 liefert, wenn dir Suchtraffic wichtig ist.

### Welchen Statuscode sollte eine Wartungsseite liefern?

503 Service Unavailable, mit einem Retry-After-Header, der sagt, wann es weitergeht. Google dokumentiert, dass 5xx-Antworten seine Crawler bremsen, ihr Inhalt verworfen wird und indexierte URLs zunächst erhalten bleiben; eine Wartungsseite mit 200 bekommt keinen dieser Schutzmechanismen.

### Wie zeige ich in einer PWA eine Offline-Seite an?

Leg beim Installieren des Service Workers eine /offline-Route in den Cache und liefere sie aus, wenn eine Navigation fehlschlägt. Für eine App, die schon offen ist, wiederholt die experimentelle Option useOffline in Next.js 16.3 fehlgeschlagene Navigationen, Prefetches und Server Actions, sobald die Verbindung wieder da ist, und der Hook useOffline aus next/offline meldet den Zustand.

---

Quelle: https://wingo-ui.com/de/blog/react-404-page
