SystemPage
The pages for not found, no access, server error, maintenance and nothing yet.
SourcePackage @kerf-js/veneer
Preview
tsx
import { SystemPage } from '@kerf-js/veneer'export function SystemPage404() { return ( <SystemPage as="h3" kind="404" onSearch={() => {}} searchLabel="Search kerf" primary={{ label: 'Go to dashboard' }} secondary={{ label: 'Report a broken link' }} meta="/projects/9921 · ref req_8f2a1c" /> )}Installation
npm install @kerf-js/veneerUsage
tsx
import { SystemPage } from '@kerf-js/veneer'<SystemPage as="h3" kind="404" onSearch={() => {}} searchLabel="Search kerf" primary={{ label: 'Go to dashboard' }} secondary={{ label: 'Report a broken link' }} meta="/projects/9921 · ref req_8f2a1c"/>No access
Asking for access moves it to pending, with Withdraw.
tsx
import { state } from '@kerf-js/core'import { SystemPage } from '@kerf-js/veneer'export function SystemPage403() { const pending = state(false) return ( <SystemPage as="h3" kind="403" autofocus={false} object={PROJECT} account="noor@kerf.dev" accounts={ACCOUNTS} onSwitch={() => {}} request={{ approvers: 'Mara and Jun', pending: pending.value, onSend: () => (pending.value = true), onWithdraw: () => (pending.value = false), }} requestId="req_7Q2K-91XF" /> )}type Status = | 'Live' | 'Building' | 'Failed' | 'Paused'const PROJECTS: { id: string name: string owner: string region: string status: Status deploys: number uptime: number latency: number cost: number updated: string}[] = [ { id: 'storefront', name: 'storefront', owner: 'Priya Raman', region: 'eu-west', status: 'Live', deploys: 412, uptime: 99.98, latency: 84, cost: 1240, updated: '2026-10-31', }, { id: 'checkout', name: 'checkout', owner: 'Tomás Oliveira', region: 'eu-west', status: 'Live', deploys: 288, uptime: 99.95, latency: 112, cost: 980, updated: '2026-10-31', }, { id: 'search-api', name: 'search-api', owner: 'Lena Fischer', region: 'us-east', status: 'Building', deploys: 196, uptime: 99.91, latency: 146, cost: 1610, updated: '2026-10-30', }, { id: 'billing', name: 'billing', owner: 'Mara Lindqvist', region: 'us-east', status: 'Live', deploys: 154, uptime: 99.99, latency: 73, cost: 720, updated: '2026-10-29', }, { id: 'mobile-bff', name: 'mobile-bff', owner: 'Sam Okafor', region: 'ap-south', status: 'Failed', deploys: 233, uptime: 99.42, latency: 208, cost: 860, updated: '2026-10-29', }, { id: 'notifier', name: 'notifier', owner: 'Jun Tanaka', region: 'eu-north', status: 'Live', deploys: 97, uptime: 99.97, latency: 58, cost: 310, updated: '2026-10-27', }, { id: 'reports', name: 'reports', owner: 'Noor Haddad', region: 'us-west', status: 'Paused', deploys: 41, uptime: 99.8, latency: 321, cost: 190, updated: '2026-10-22', }, { id: 'edge-cache', name: 'edge-cache', owner: 'Jun Tanaka', region: 'global', status: 'Live', deploys: 318, uptime: 100, latency: 21, cost: 1430, updated: '2026-10-31', }, { id: 'auth', name: 'auth', owner: 'Mara Lindqvist', region: 'eu-west', status: 'Live', deploys: 176, uptime: 99.99, latency: 66, cost: 540, updated: '2026-10-28', }, { id: 'ingest', name: 'ingest', owner: 'Lena Fischer', region: 'us-east', status: 'Live', deploys: 264, uptime: 99.93, latency: 131, cost: 2120, updated: '2026-10-30', },]const ACCOUNTS = [ { id: 'mara', email: 'mara@kerf.dev', detail: 'kerf · this workspace', current: true, }, { id: 'side', email: 'mara@sample.dev', detail: 'Sample Co · has access', hasAccess: true, },]const PROJECT = { title: PROJECTS[3]!.name, owner: PROJECTS[3]!.owner,}Server error
Pair it with <Boundary>. The error's message is never shown.
tsx
import { state } from '@kerf-js/core'import { SystemPage } from '@kerf-js/veneer'export function SystemPage500() { const tries = state(0) return ( <SystemPage as="h3" kind="500" onRetry={() => tries.value++} secondary={{ label: 'Status page' }} requestId={`req_1a9f-${tries.value}`} /> )}Maintenance
Use kind set to maintenance with backAt to say when the service returns.
tsx
import { SystemPage } from '@kerf-js/veneer'export function Maintenance() { return ( <SystemPage as="h3" kind="maintenance" backAt="14:00 UTC" secondary={{ label: 'Status page' }} /> )}Nothing yet
Use kind set to empty with primary and secondary to offer a first step.
tsx
import { SystemPage } from '@kerf-js/veneer'export function Empty() { return ( <SystemPage as="h3" kind="empty" title="No projects yet" description={firstDeployDescription} primary={{ label: 'New project' }} secondary={{ label: 'Import' }} /> )}const firstDeployDescription = 'Connect a repository and your first ' + 'deploy runs in about a minute.'Accessibility
- This has no shortcuts of its own: controls inside it use Tab, Enter and Space as usual, and veneer's parts keep their roles and labels.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
kind | "empty" | "404" | "403" | "500" | "maintenance" | — | Which page. |
title | unknown | — | Replaces the heading. |
description | unknown | — | Replaces the line under it. |
code | unknown | — | The small line above the heading. |
primary | { label: unknown; onClick?: (() => void) | undefined; href?: string | un… | — | The main action. |
secondary | { label: unknown; onClick?: (() => void) | undefined; href?: string | un… | — | The quieter action. |
searchLabel | string | — | The 404 search field's label. Default "Search". |
error | unknown | — | The error, for the Boundary form; its message is never shown. |
requestId | string | — | The support line. |
meta | unknown | — | Extra support text: a path, an incident. |
backAt | unknown | — | Maintenance: "Back at 14:00 UTC", and the local-time line in description. |
object | { title: string; owner?: string | undefined; } | — | 403: what was asked for and by whom. |
account | string | — | 403: the signed-in account, as in "You're signed in as ana@kerf.dev". |
request | AccessRequest | — | 403: the access request: who approves it, whether it is pending, and what sending or withdrawing does. |
accounts | SwitchAccount[] | — | 403: other signed-in accounts, offered in a Switch account menu. |
granted | boolean | false | 403: access came through: children is the object, loaded in place. |
as | "h2" | "h3" | — | The heading level. |
autofocus | boolean | — | Focus the title on load, so the route change is heard (default: 403). |
children | unknown | — | What it contains. |
| Event | Type | Default | Description |
|---|---|---|---|
onSearch | ((q: string) => void) | — | 404: the search field. |
onRetry | (() => void) | — | 500: retry from <Boundary>. |
onSwitch | ((id: string | undefined) => void) | — | Called with the account picked, or undefined to sign in as someone else. |
Parts
Every element carries a stable name, so you can style it from your own CSS: [data-scope="system-page"] [data-part="body"]
bodysearchactionsmeta
RelatedConn bannerRate limited