Boundaries

Markdown

Error- und Loading-Boundaries sind ein zentrales Werkzeug, um Ladezustände anzuzeigen und Fehler im UI gezielt abzufangen. Sie verhindern, dass Fehler unkontrolliert die gesamte Anwendung zum Absturz bringen, und geben Usern eine verständliche Rückmeldung. Gleichzeitig unterstützen sie Entwickler durch Logging und Monitoring bei der Fehleranalyse.

Es gibt kein universelles Schema, wo Boundaries am besten sitzen. Diese Seite führt vom Prinzip über die Entscheidung bis zu konkreten Rezepten.


Grundlagen

Zwei Primitive

Error- und Loading-Boundary sind zwei getrennte Konzepte, ausgedrückt über zwei Wrapper-Typen:

PrimitivEnthältZweck
…WithBoundariesLoading + ErrorStandardfall; an der Seiten-Wurzel und für enge Loading-Ausnahmen.
…WithErrorBoundarynur ErrorEnges Error-Scoping für Sekundärdaten. Der Loading-Zustand blubbert nach oben.

Eine reine Loading-Boundary gibt es bewusst nicht.

Wann eine Boundary nötig ist

Liest eine Component eine Datenquelle, die Suspense auslöst (react-use-promise / Ghost-.use(), eine Suspense auslösende Query), muss sie von Boundaries abgedeckt sein.

Umkehrung: kein Suspense → keine Boundary. Löst der Render-Prop kein Suspense aus (i18n-useT…(), Props und bereits oben geladene Cache-Hits zählen nicht), braucht die Component gar keine Boundary – dann das plain Flow-Primitiv verwenden (<LayoutCard>, <Section>), nicht …WithErrorBoundary.

Die richtige Boundary wählen

Drei Fragen führen zur passenden Boundary:

  1. Löst der Inhalt Suspense aus? Nein → keine Boundary, plain Primitiv. Ja → weiter.
  2. Sind die Daten primär oder sekundär? Primär (Seite ohne sie sinnlos) → keine eigene Boundary, die weite Boundary an der Seiten-Wurzel fängt den Fehler. Sekundär → eigene enge Boundary.
  3. Wie soll der Bereich laden? Sichtbar Platz halten → kombinierte …WithBoundaries mit Skeleton. Nur Fehler abfangen (Loading blubbert nach oben) → …WithErrorBoundary. Bedingt/unsichtbar (null möglich) → bare Fragment….

Error-Scope: Kritikalität der Daten

  • Primär – die Seite wäre ohne diese Daten sinnlos oder leer (Faustregel: die Ressource des Routen-Parameters, z. B. der Cronjob auf $cronjobId). → Keine eigene enge Error-Boundary; der Fehler fällt an die weite Boundary an der Seiten-Wurzel.
  • Sekundär – die Seite bleibt ohne diese Daten nutzbar (z. B. eine verknüpfte App-Installation). → Eigene enge …WithErrorBoundary, damit ein Fehler nur diesen Teil betrifft.

Einmalig genutzte Sections (nur zur Lesbarkeit aufgeteilte -components/-Sektionen, die ausschließlich die Primär-Ressource lesen) bekommen darum keine eigene Boundary – die Seiten-Wurzel deckt sie ab. Eine eigene behält man nur, wenn die Section Sekundärdaten liest, mehrfach oder geteilt genutzt wird (src/shared/, Listenelemente) oder ein Modal / Kontextmenü ist, das erst beim Öffnen lädt (siehe Modals).

Loading-Scope: Layout-Shift vermeiden

  • Default: weit. An der Seiten-Wurzel sitzt immer eine kombinierte …WithBoundaries (Loading und Error), oberhalb aller Sections – wenige, große Ladezustände statt vieler kleiner.
  • Ausnahme: eng für lang ladende, unabhängige Elemente (z. B. Metriken auf Dashboards). Hier ist ein enger Error-Scope ohnehin erwünscht, daher nutzt man eine enge kombinierte …WithBoundaries.

Darstellung

  • Loading, sichtbarform-treuer Skeleton, der den finalen Platz reserviert. Weder Spinner noch impliziter null – beide erzeugen den Layout-Shift, den die weite Boundary verhindern soll.
  • Loading, bedingt/unsichtbar (kann nach dem Laden null sein) → null ist korrekt; ein Skeleton würde als Phantom aufblitzen. Das ist der Fall des bare Fragment…-Wrappers.
  • Error, sichtbarer Sekundär-Content → sichtbarer degradierter Zustand (kompakter Inline-Fehler), nie stumm verschwinden.
  • Error, primär/weit → volle IllustratedMessage an der Seiten-Wurzel.

Stummes null-on-error ist nur für den bare Fragment-Wrapper erlaubt; Sentry protokolliert den Fehler ohnehin – es geht rein um die UX.

Layout-Primitiv bewahren

Jeder Wrapper trägt neben dem Verhalten ein Layout-Primitiv: Section… rendert <Section> (Abstände), LayoutCard… rendert <LayoutCard> (Card-Chrome), Fragment… rendert kein strukturelles Element. Bei einem Scope-Wechsel darf dieses Primitiv nicht stillschweigend verloren gehen – z. B. beim Wechsel SectionWithBoundariesFragmentWithBoundaries die Kinder explizit in eine <Section> wrappen.

Reset / Retry

Reset bei Routenwechsel (resetKeys) plus QueryErrorResetBoundary; der Error-Fallback bietet ein Retry an, wenn der Platz es zulässt. Die Aktion sollte zum Scope passen – ein enges Sekundär-Widget darf nicht die ganze App neu laden.

Rezepte

Seiten-Wurzel: LayoutCard

Sind Funktionen oder Informationen einer LayoutCard essenziell für die Seite, umschließt die gesamte LayoutCard eine LayoutCardWithBoundaries.

Detailseite

Die Primär-Ressource des Routen-Parameters wird zuerst geladen, damit im Fehlerfall die ganze Seite in die ErrorView wechselt. Die einzelnen …Section-Components lesen nur diese Ressource und bekommen daher keine eigene Boundary (siehe Error-Scope).

Tab

Verteilt eine Seite Inhalte auf mehrere Tabs, sichert man sie mit SectionsFragmentWithBoundaries einzeln ab.

Section mit Sekundärdaten

Enthält eine Section ergänzende Sekundär-Daten, die das primäre Nutzererlebnis nicht einschränken, lohnt sich eine enge Error-Boundary über SectionWithErrorBoundary: Ein Fehler bleibt lokal, während der Loading-Zustand zur Seiten-Wurzel blubbert. Lädt der Bereich unabhängig und sichtbar (eigener Skeleton erwünscht), nimmt man stattdessen die kombinierte SectionWithBoundaries.

Sonderfall: Wird eine Section nur bedingt angezeigt, soll keine eigene Loading View entstehen, damit User kein Ladeverhalten sehen, das zu nichts Sichtbarem führt. Dafür eignet sich FragmentWithBoundaries – es schützt die Section ohne sichtbaren Ladezustand; das Layout-Primitiv (<Section>) bleibt bewusst erhalten.

Modals

Modals und Kontextmenüs öffnen erst auf Interaktion – eine seiteninterne Loading-Boundary ergäbe hier keinen Sinn, den Ladezustand trägt die Flow-Component selbst. Deshalb sind ModalWithErrorBoundary und ContextMenuWithErrorBoundary reine Error-Boundaries (disableSuspenseBoundary); ein kombiniertes Pendant gibt es bewusst nicht. ModalWithErrorBoundary verhindert außerdem, dass Daten im Modal schon vor dem Öffnen nachgeladen werden.

Forms in Modals

Eine teilweise geladene oder fehlerhafte Form untergräbt das Vertrauen der User. Sobald in der Form ein Fehler auftritt, fällt das ganze Modal in die ErrorView – auch wenn es darin noch Boundaries gibt.

Diagramm

Diagramme beziehen viele Daten aus externen Services und sind fehleranfällig – sie gehören immer in eine Boundary. Für das CartesianChart gibt es ChartFragmentWithBoundaries.

Einzelne Elemente

Kleine Elemente wie Texte, Links, Badges oder ContextMenus ergänzen die Seite nur und werden separat geschützt, um sie nicht komplett zu blockieren. Sie besitzen oft keine eigene ErrorView und erscheinen im Fehlerfall nicht.

Text / String

TextWithBoundaries bzw. StringWithBoundaries – je nachdem, ob am Ende eine Text-Component oder nur ein String gerendert wird.

Zusätzlich gibt es Components, die direkt auf ein Model angewendet werden und eine eingebaute Boundary besitzen.

Alerts / Badges

Alerts und Badges ergänzen eine Seite nur um Hinweise oder Status. Sie kommen in ein FragmentWithBoundaries – geschützt, ohne eigene Loading View (der Fall „bedingter / unsichtbarer Content" mit null-Fallback).

Field

FieldFragmentWithBoundaries zeigt im Loading State ein Textfeld, dem optional ein Label mitgegeben werden kann.

ContextMenu

ContextMenuWithErrorBoundary sichert das Menü ab. Damit sein Inhalt nicht springt, kommen Elemente mit nachgeladenen Inhalten in ein MenuItemFragmentWithBoundaries; diese Boundary enthält kein Suspense, damit das ganze ContextMenu im Ladezustand verbleibt.

Verfügbare Wrapper

Der Component-Name ergibt sich aus dem Element plus WithBoundaries (kombiniert: Loading + Error) bzw. WithErrorBoundary (nur Error). Nicht jedes Element hat beide Varianten:

ElementKombiniert (…WithBoundaries)Nur Error (…WithErrorBoundary)
LayoutCard
LayoutCardFragment
Section
SectionsFragment
ContentFragment
Fragment
Text
String
Link
LinkFragment
FieldFragment
ChartFragment
Avatar
MenuItemFragment
Modal
ContextMenu

Weitere Empfehlungen

Ein durchdachtes Boundary-Konzept endet nicht bei der technischen Umsetzung. Fehler sollten zuverlässig an Monitoring-Systeme wie Sentry oder Datadog weitergegeben werden, damit Probleme früh sichtbar werden. Manuelle und automatisierte Tests prüfen regelmäßig, dass die Fallbacks in allen relevanten Situationen greifen. Ebenso wichtig ist eine klare, konsistente Formulierung der Fehlermeldungen – konkrete Hinweise dazu gibt die Guideline Fehlermeldungen.

Auf dieser Seite