SystemPage
The pages for not found, no access, server error, maintenance and nothing yet.
SourcePackage @weft-js/warp
Preview
tsx
import { SystemPage } from '@weft-js/warp'export function SystemPage404() { return ( <SystemPage as="h3" kind="404" onSearch={() => {}} searchLabel="Search weft" primary={{ label: 'Go to dashboard' }} secondary={{ label: 'Report a broken link' }} meta="/projects/9921 · ref req_8f2a1c" /> )}Installation
npm install @weft-js/warpUsage
tsx
import { SystemPage } from '@weft-js/warp'<SystemPage as="h3" kind="404" onSearch={() => {}} searchLabel="Search weft" 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 '@weft-js/core'import { SystemPage } from '@weft-js/warp'export function SystemPage403() { const pending = state(false) return <SystemPage as="h3" kind="403" autofocus={false} object={PROJECT} account="noor@weft.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@weft.dev', detail: 'weft · 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 '@weft-js/core'import { SystemPage } from '@weft-js/warp'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 '@weft-js/warp'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 '@weft-js/warp'export function Empty() { return ( <SystemPage as="h3" kind="empty" title="No projects yet" description="Connect a repository and your first deploy runs in about a minute." primary={{ label: 'New project' }} secondary={{ label: 'Import' }} /> )}Accessibility
- This has no shortcuts of its own: controls inside it use Tab, Enter and Space as usual, and warp'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@weft.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