Boundaries
MarkdownError- 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:
| Primitiv | Enthält | Zweck |
|---|---|---|
…WithBoundaries | Loading + Error | Standardfall; an der Seiten-Wurzel und für enge Loading-Ausnahmen. |
…WithErrorBoundary | nur Error | Enges 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:
- Löst der Inhalt Suspense aus? Nein → keine Boundary, plain Primitiv. Ja → weiter.
- 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.
- Wie soll der Bereich laden? Sichtbar Platz halten → kombinierte
…WithBoundariesmit Skeleton. Nur Fehler abfangen (Loading blubbert nach oben) →…WithErrorBoundary. Bedingt/unsichtbar (nullmöglich) → bareFragment….
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, sichtbar → form-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
nullsein) →nullist korrekt; ein Skeleton würde als Phantom aufblitzen. Das ist der Fall des bareFragment…-Wrappers. - Error, sichtbarer Sekundär-Content → sichtbarer degradierter Zustand (kompakter Inline-Fehler), nie stumm verschwinden.
- Error, primär/weit → volle
IllustratedMessagean 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 SectionWithBoundaries → FragmentWithBoundaries 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:
| Element | Kombiniert (…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.