Offline screen built around a live sync queue: a display headline with a retry pill and a self-restarting countdown that raises the attempt number, beside a soft panel listing every change held on the device with a pulsing listening indicator.
An offline screen built around what is still waiting to send: the statement and a retry pill sit beside a soft panel listing every change held on the device, with a countdown that restarts itself and raises the attempt number each time it lapses.
Pro blocks install through the shadcn CLI with your license key and ship their full source. Docs and live previews stay open to everyone, so you can read every block's details first.
Swap YOUR_EMAIL and YOUR_KEY for the email and license key on your account. Find your license key on your account page.
Radix flavor
npx shadcn add "https://ui.beste.co/r/error31?email=YOUR_EMAIL&license_key=YOUR_KEY"Base UI flavor
npx shadcn add "https://ui.beste.co/r-base/error31?email=YOUR_EMAIL&license_key=YOUR_KEY"This installs the block to components/beste/block/error31.tsx, plus the two design-system primitives it renders through: badge7 for the parenthetical eyebrow and button12 for the retry pill.
The installed file exports error31Demo alongside the block: the exact props behind the preview above. Spread it and the retry loop starts immediately.
import { Error31, error31Demo } from "@/components/beste/block/error31";
export default function Offline() {
return <Error31 {...error31Demo} />;
}Feed the queue from your own outbox and let the loop drive the reconnect attempt:
"use client";
import { Error31 } from "@/components/beste/block/error31";
export default function Offline({ pending }: { pending: { title: string; meta: string }[] }) {
return (
<Error31
badge={{ label: "No Connection" }}
heading="You are working <strong>with the door shut</strong>."
description="The network stopped answering, so nothing is leaving this device."
labels={{
retry: "Knock again",
countdown: "Trying again in",
seconds: "seconds",
attempt: "Attempt",
queueTitle: "Held on this device",
queueStatus: "Listening for the network",
note: "Closing the tab is safe. The queue survives until it clears.",
}}
retrySeconds={20}
queue={pending.map((item) => ({ ...item, state: "Queued" }))}
onRetry={() => flushOutbox()}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
badge | { label: string } | – | Parenthetical eyebrow above the headline |
heading | string | – | Headline HTML, rendered as an h1; <strong> marks the muted clause |
description | string | – | Explanatory paragraph under the headline |
labels | Error31Labels | {} | Retry text, countdown sentence, unit word, attempt word, and the panel captions |
retrySeconds | number | – | Interval of the automatic retry loop |
queue | QueueItem[] | [] | Changes held on the device |
onRetry | () => void | – | Called on every automatic lapse and on every manual press |
className | string | – | Extra classes for the outer <section> |
type Error31Labels = {
retry?: string;
countdown?: string;
seconds?: string;
attempt?: string;
queueTitle?: string;
queueStatus?: string;
note?: string;
};
type QueueItem = {
title: string;
meta?: string;
state: string;
};onRetry, resets itself to retrySeconds and increments the attempt number, so the page keeps probing for a connection on its own.onRetry, so a manual press and an automatic lapse are indistinguishable to the handler.retrySeconds (or 0) disables the loop entirely. The countdown line disappears and the pill becomes a plain manual retry.animate-pulse utility, a plain CSS animation with no JavaScript behind it and no custom keyframe to install.aria-live="polite", so screen readers hear the remaining time and the attempt number without the page grabbing focus.queue is non-empty, which turns the block into a plain offline statement when nothing is pending.error22
Monochrome maintenance page: a parenthetical eyebrow over a display headline, the downtime window as a ruled three-column record, and a rounded notify-me field with a confirmed state.
error17
Planned downtime page with a square eyebrow and status code, a bold accent headline, three bordered cells for the maintenance window, a working notify-me email form with a success state, and a live progress link.
error8
404 page that counts down to an automatic redirect and calls onRedirect once it reaches zero, with a cancel button that stops the timer and a manual go-now link.
error10
Screen-height 404 with a full-bleed background photo under a dark overlay, centered white messaging, and a solid plus outline button pair sized for marketing sites.
error23
Monochrome permission page: a display headline over a request pill that swaps to a sent confirmation, a ruled record of the project, owner and visibility, and an overlapping avatar row of current members.
error6
The smallest possible 404: the status code and the message sit on one line separated by a vertical rule, stacking on mobile, with a single outline button underneath.