Error Boundaries

Estimated reading time: 2 minutes

Error boundaries catch errors thrown by async components and render a fallback instead of crashing the entire tree.

Usage

Import ErrorBoundary from @kitajs/html/error-boundary and wrap async components. The catch prop accepts a JSX element or a function that receives the error.

async function 
function UserProfile({ id }: {
    id: string;
}): Promise<string>
UserProfile
({
id: string
id
}: {
id: string
id
: string }) {
const
const user: {
    name: string;
}
user
= await
let db: {
    getUser(id: string): Promise<{
        name: string;
    }>;
}
db
.
function getUser(id: string): Promise<{
    name: string;
}>
getUser
(
id: string
id
) // may reject
return <
JSX.IntrinsicElements.div: JSX.HtmlTag
div
JSX.HtmlTag.safe?: boolean | undefined

When set to true, all inner content (html or not) of this tag will be escaped when evaluated.

Warning: This will escape even inner jsx tags. You should only use this in the most inner tag of the html tree.

@example
<div>{'<script />'}</div>
'<div><script /></div>'
<div safe>{'<script />'}</div>
'<div><script /></div>'
<div><div>{'<script />'}</div></div>
'<div><div><script /></div></div>'

// Escapes even inner jsx tags
<div safe><div>{'<script />'}</div></div>
'<div><div><script /></div></div>'
@defaultfalse@seehttps ://github.com/kitajs/html/tree/master/packages/html#sanitization
safe
>{
const user: {
    name: string;
}
user
.
name: string
name
}</
JSX.IntrinsicElements.div: JSX.HtmlTag
div
>
} const
const html: string
html
= await (
<
function ErrorBoundary(props: ErrorBoundaryProps): JSX.Element

A component that adds an error boundary to catch any inner promise rejection.

ErrorBoundary
ErrorBoundaryProps.catch: JSX.Element | ((error: unknown) => JSX.Element)

The error boundary to use if the async children throw an error.

The error will be string timeout if the rejection was caused by the timeout property.

If the timeout gets triggered, it will throw an {@linkcode HtmlTimeout } error.

catch
={(
err: unknown
err
) => <
JSX.IntrinsicElements.div: JSX.HtmlTag
div
>Error: {
var String: StringConstructor
(value?: any) => string

Allows manipulation and formatting of text strings and determination and location of substrings within strings.

String
(
err: unknown
err
)}</
JSX.IntrinsicElements.div: JSX.HtmlTag
div
>}>
<
function UserProfile({ id }: {
    id: string;
}): Promise<string>
UserProfile
id: string
id
="123" />
</
function ErrorBoundary(props: ErrorBoundaryProps): JSX.Element

A component that adds an error boundary to catch any inner promise rejection.

ErrorBoundary
>
)

If UserProfile throws, the boundary renders the catch fallback. If it succeeds, the boundary renders the component output unchanged.

Sync components

Error boundaries only catch errors from async components, because synchronous errors propagate before the boundary can intercept them. For synchronous components, use a standard try/catch.

function 
function SyncComponent(): JSX.Element
SyncComponent
() {
try { const
const data: string
data
=
function riskyOperation(): string
riskyOperation
()
return <
JSX.IntrinsicElements.div: JSX.HtmlTag
div
>{
const data: string
data
}</
JSX.IntrinsicElements.div: JSX.HtmlTag
div
>
} catch (
function (local var) err: unknown
err
) {
return <
JSX.IntrinsicElements.div: JSX.HtmlTag
div
>Error: {
var String: StringConstructor
(value?: any) => string

Allows manipulation and formatting of text strings and determination and location of substrings within strings.

String
(
function (local var) err: unknown
err
)}</
JSX.IntrinsicElements.div: JSX.HtmlTag
div
>
} }
<div>Loaded safely</div>

Combining with Suspense

Error boundaries and Suspense serve different purposes and are used together. Suspense's catch prop handles errors from its async children. An ErrorBoundary wrapping the Suspense handles errors from the fallback itself.

const 
const html: JSX.Element
html
= (
<
function ErrorBoundary(props: ErrorBoundaryProps): JSX.Element

A component that adds an error boundary to catch any inner promise rejection.

ErrorBoundary
ErrorBoundaryProps.catch: JSX.Element | ((error: unknown) => JSX.Element)

The error boundary to use if the async children throw an error.

The error will be string timeout if the rejection was caused by the timeout property.

If the timeout gets triggered, it will throw an {@linkcode HtmlTimeout } error.

catch
={<
JSX.IntrinsicElements.div: JSX.HtmlTag
div
>Fallback failed</
JSX.IntrinsicElements.div: JSX.HtmlTag
div
>}>
<
function Suspense(props: SuspenseProps): JSX.Element

A component that returns a fallback while the async children are loading.

The rid prop is the one {@linkcode renderToStream } returns, this way the suspense knows which request it belongs to.

Warning: Using Suspense without any type of runtime support will LEAK memory and not work. Always use with renderToStream or within a framework that supports it.

Suspense
SuspenseProps.rid: string | number

The request id is used to identify the request for this suspense.

rid
={
const rid: "req-1"
rid
}
SuspenseProps.fallback: JSX.Element

The fallback to render while the async children are loading.

fallback
={<
function AsyncFallback(): Promise<string>
AsyncFallback
/>}
SuspenseProps.catch?: JSX.Element | ((error: unknown) => JSX.Element) | undefined

This error boundary is used to catch any error thrown by an async component and streams its fallback instead.

Undefined behaviors happens on each browser kind when the html stream is unexpected closed by the server if an error is thrown. You should always define an error boundary to catch errors.

This does not catches for errors thrown by the suspense itself or async fallback components. Please use {@linkcode ErrorBoundary } to catch them instead.

catch
={<
JSX.IntrinsicElements.div: JSX.HtmlTag
div
>Children failed</
JSX.IntrinsicElements.div: JSX.HtmlTag
div
>}>
<
function AsyncContent(): Promise<string>
AsyncContent
/>
</
function Suspense(props: SuspenseProps): JSX.Element

A component that returns a fallback while the async children are loading.

The rid prop is the one {@linkcode renderToStream } returns, this way the suspense knows which request it belongs to.

Warning: Using Suspense without any type of runtime support will LEAK memory and not work. Always use with renderToStream or within a framework that supports it.

Suspense
>
</
function ErrorBoundary(props: ErrorBoundaryProps): JSX.Element

A component that adds an error boundary to catch any inner promise rejection.

ErrorBoundary
>
)

If AsyncContent throws, Suspense renders "Children failed". If AsyncFallback throws, the ErrorBoundary renders "Fallback failed". These are independent error paths.