Templates
Status pages
Whole-page states outside the app layout; not found (404), server error (500) and planned maintenance.
Status pages show when the app layout cannot load or when someone reaches an address that does not exist. All three are built from one ResultState: what happened, why, and the next step. There is no navigation; only the product name and the content.
Preview
Section titled “Preview”On the 500 page, press “Try again”: the button turns busy, the demo fails again and the result is read. The brand and mode switches in the page header restyle the preview too.
pnpm add @bigbang/ui| Data | Type | Note |
|---|---|---|
kind |
'not-found' | 'server-error' | 'maintenance' |
The page and its ResultState tone: info, danger, warning. |
onRetry |
() => Promise<boolean> |
“Try again” on the 500 page; when it returns true, the app reloads the page. |
| Reference | string |
The trace code from the server; support searches by it. |
The server returns these pages with the right HTTP status (404, 500, 503); the content only tells people what happened.
import { Button, Icon, Link, ProgressBar, ResultState, Stack, Text } from '@bigbang/ui';import { useState } from 'react';import { type Lang, LangProvider } from '../../patterns/lang';
const copy = { tr: { product: 'Ürün adı', home: 'Ana sayfaya dön', back: 'Önceki sayfa', code: 'Hata kodu', reference: 'Başvuru kodu', 'not-found': { title: 'Sayfa bulunamadı', description: 'Adres yanlış yazılmış ya da sayfa taşınmış olabilir. Adresi kontrol edin veya ana sayfadan devam edin.', }, 'server-error': { title: 'Bir sorun oluştu', description: 'İsteğiniz şu an tamamlanamadı. Birkaç saniye sonra yeniden deneyin; sorun sürerse başvuru koduyla destek ekibine yazın.', }, maintenance: { title: 'Bakım çalışması sürüyor', description: 'Hizmeti iyileştirmek için kısa bir bakım yapıyoruz. Çalışmanız kaydedildi; bakım bitince kaldığınız yerden devam edebilirsiniz.', }, retry: 'Yeniden dene', retrying: 'Yeniden deneniyor', stillFailing: 'Hâlâ yanıt alınamıyor. Biraz sonra yeniden deneyin.', support: 'Destek ekibine yaz', progress: 'Bakım ilerlemesi', progressNote: 'Tahmini bitiş 14:00', statusPage: 'Durum sayfası', }, en: { product: 'Product name', home: 'Go to the home page', back: 'Previous page', code: 'Error code', reference: 'Reference', 'not-found': { title: 'Page not found', description: 'The address may be mistyped or the page may have moved. Check the address or go on from the home page.', }, 'server-error': { title: 'Something went wrong', description: 'Your request could not be completed right now. Try again in a few seconds; if it keeps failing, write to support with the reference.', }, maintenance: { title: 'Maintenance in progress', description: 'We are doing a short maintenance to improve the service. Your work is saved; you can carry on where you left off once it ends.', }, retry: 'Try again', retrying: 'Trying again', stillFailing: 'Still no response. Try again a little later.', support: 'Write to support', progress: 'Maintenance progress', progressNote: 'Expected to end at 14:00', statusPage: 'Status page', },};
export type StatusPageKind = 'not-found' | 'server-error' | 'maintenance';
export type StatusPagesBlockProps = { lang?: Lang; /** Which page: 404, 500 or planned maintenance. */ kind?: StatusPageKind; /** Runs on "Try again" of the 500 page; resolve `true` when the request worked. The demo always fails. */ onRetry?: () => Promise<boolean>;};
const tones = { 'not-found': 'info', 'server-error': 'danger', maintenance: 'warning' } as const;const demoRetry = () => new Promise<boolean>((resolve) => setTimeout(() => resolve(false), 1000));
/** * Whole-page states outside the app layout: page not found (404), server error (500) with a retry * and a reference, and planned maintenance with progress. Each page has one `h1` from ResultState. */export function StatusPagesBlock({ lang = 'tr', kind = 'not-found', onRetry = demoRetry }: StatusPagesBlockProps) { const t = copy[lang]; const [retrying, setRetrying] = useState(false); const [failedAgain, setFailedAgain] = useState(false);
const retry = async () => { setRetrying(true); setFailedAgain(false); const ok = await onRetry(); setRetrying(false); setFailedAgain(!ok); };
const actions = { 'not-found': ( <> <Button render={<a href="#" />} nativeButton={false} startIcon={<Icon name="home" />}> {t.home} </Button> <Button variant="secondary" startIcon={<Icon name="arrow-left" />} onClick={() => window.history.back()}> {t.back} </Button> </> ), 'server-error': ( <> <Button startIcon={<Icon name="refresh" />} loading={retrying} loadingLabel={t.retrying} onClick={() => void retry()}> {t.retry} </Button> <Button render={<a href="#" />} nativeButton={false} variant="secondary"> {t.home} </Button> </> ), maintenance: ( <Link href="#" external> {t.statusPage} </Link> ), }[kind];
return ( <LangProvider lang={lang}> <div className="flex min-h-[36rem] w-full flex-col bg-surface-default" data-template="status-page"> <header className="border-b border-line-subtle px-lg py-md"> <strong className="type-label-md text-fg-default">{t.product}</strong> </header> <main className="flex flex-1 items-center justify-center px-lg py-4xl"> <ResultState tone={tones[kind]} title={t[kind].title} description={t[kind].description} actions={actions} className="max-w-[32rem]"> {kind === 'not-found' ? ( <Text size="sm" tone="subtle" numeric>{`${t.code}: 404`}</Text> ) : kind === 'server-error' ? ( <Stack gap="sm" align="center"> <Text size="sm" tone="subtle" numeric>{`${t.code}: 500 · ${t.reference}: REF-0001`}</Text> <Text size="sm" tone="danger" role="status"> {failedAgain ? t.stillFailing : ''} </Text> <Link href="#" size="sm"> {t.support} </Link> </Stack> ) : ( <div className="w-full min-w-[16rem]"> <ProgressBar label={t.progress} value={60} description={t.progressNote} /> </div> )} </ResultState> </main> </div> </LangProvider> );}Structure
Section titled “Structure”- Only the product name at the top; no navigation or search.
- A ResultState centred in
main, its title anh1. - 404 (
info): “Go to the home page” primary, “Previous page” secondary; “Error code: 404” below. - 500 (
danger): “Try again” (busy withloading), “Go to the home page”; error code and reference, a polite status line and a “Write to support” link. - Maintenance (
warning): a ProgressBar and the expected end; an external “Status page” link.
Motion
Section titled “Motion”The ResultState icon grows in with motion.success; the progress bar fills with motion.progress. “Try again” shrinks on press with motion.press and turns into a spinner. With reduced motion, scaling stops.
Accessibility
Section titled “Accessibility”- Each page has one
mainand oneh1. ResultState adds the tone as a hidden prefix to the title: “Error: Something went wrong”; meaning is never given by colour and icon alone. - “Try again” keeps its name while busy and “Trying again” is read once; a second failure is read from the polite status line.
- Error codes and the reference are set
numericand can be copied for support. - Maintenance progress has the
progressbarrole; its value is read as a percentage and the expected end as its description. - “Status page” is an external link; screen readers hear that it opens in a new tab.
Components used
Section titled “Components used”ResultState, Button, Link, ProgressBar, Text. Patterns: Error recovery, Loading.