Skip to content
kerfjsGitHub

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/veneer

Usage

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

PropTypeDefaultDescription
kind"empty" | "404" | "403" | "500" | "maintenance"—Which page.
titleunknown—Replaces the heading.
descriptionunknown—Replaces the line under it.
codeunknown—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.
searchLabelstring—The 404 search field's label. Default "Search".
errorunknown—The error, for the Boundary form; its message is never shown.
requestIdstring—The support line.
metaunknown—Extra support text: a path, an incident.
backAtunknown—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.
accountstring—403: the signed-in account, as in "You're signed in as ana@kerf.dev".
requestAccessRequest—403: the access request: who approves it, whether it is pending, and what sending or withdrawing does.
accountsSwitchAccount[]—403: other signed-in accounts, offered in a Switch account menu.
grantedbooleanfalse403: access came through: children is the object, loaded in place.
as"h2" | "h3"—The heading level.
autofocusboolean—Focus the title on load, so the route change is heard (default: 403).
childrenunknown—What it contains.
EventTypeDefaultDescription
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