UIProvider mounts a boundary at the root, so every app has one without doing anything.
What the root boundary does
The boundary sorts each crash into one of three kinds and handles each differently:
The screen is in English or Turkish, following the shell language. It is translated on its own, so it still works if the crash took your app’s i18n down with it.
Keeping a crash local
The root boundary replaces the whole app. Add your ownAppErrorBoundary around a part of the screen so a crash there leaves the header and tab bar working.
unknown[]
Clear the error when any of these change. Pass the route so leaving the broken screen recovers it without a reload.
'app' | 'section'
default:"'section'"
section sits inside your own layout. app fills the viewport and offers to close the app in a shell; the root boundary uses it.ReactNode | (props: ErrorFallbackProps) => ReactNode
Replace the default screen. A function receives
error, kind, scope, reset() and reload().Partial<ErrorFallbackLabels>
Override the default screen’s text, for example with your app’s own translations:
renderTitle, renderBody, chunkTitle, chunkBody, networkTitle, offlineTitle, networkBody, retry, reload, close, details.string
Force the default screen’s language. Defaults to the shell language.
(report: AppErrorReport) => void
Called after the crash is reported.
UIProvider: <UIProvider errorBoundary={{ labels, onError }}>. Pass errorBoundary={false} if you mount your own at the root.
ErrorFallback is the default screen as a component, for when you want to render it yourself.
A query that failed to load
A crash is different from a request that didn’t load. For the second, renderErrorState where the content would be. It looks different from an empty state on purpose: “nothing here yet” and “this didn’t load” ask the user for opposite things.
unknown
The failure. When it says the user hasn’t turned your app on (
403 APP_NOT_ACTIVATED), the state says so and hides the retry button, since retrying can’t fix that.() => void
Shows a retry button. Re-run the query rather than reloading the page, so anything playing keeps playing.
IconName | ReactNode
Icon above the title.
ButtonVariant
default:"'ghost'"
Retry button style.
Partial<{ title, description, retry, notActivatedTitle, notActivatedDescription }>
Text overrides.
string
Force English or Turkish.
Listening for errors
Every crash a boundary catches, every uncaught error and every unhandled promise rejection is logged and passed toonAppError listeners from @cskn/sdk. Use it to send crashes to your own error tracker.
'chunk' | 'network' | 'render'
The kind, as in the table above.
'app' | 'section' | 'global'
Which boundary caught it.
global means no boundary saw it: a window error or an unhandled rejection.string
React’s component stack, when a boundary caught it.
string
Your app’s slug and deployed version.
reportAppError(error, { scope }) reports an error you caught yourself, classifyAppError(error) returns its kind, and reloadAppOnce() reloads unless it already did in the last 15 seconds. UIProvider installs the global handlers; without it, call installAppErrorHandlers() once at startup.