Error Handling
Bad links, invalid parameters, a busy blockchain API, or no connection at all: each one gets a clear, on-brand page that explains what happened and points the way back, in both light and dark themes.
Pages that do not exist
A global 404 page handles any unmatched URL with an inline hash search and quick links to the main flows. The dynamic routes validate their parameter on the server before rendering and call notFound() when it is malformed, so a bad link lands on a specific message instead of a broken view:
/v/[hash]checks the hash is 64 hexadecimal characters./u/[address]checks the address is a valid Stacks principal./groups/[id]checks the id is a number.
Next.js automatically adds noindex for pages that return a 404, so these never show up in search results.
When something breaks
Every route segment that fetches on-chain data has an error.tsx boundary. It shows a retry button wired to unstable_retry(), which re-runs the failed segment, plus a contextual link back. The global error page adds a link to report the issue, and the original error message is shown only in development. When the failure looks like a rate limit, a dedicated view counts down and retries on its own.
Offline and maintenance
The service worker pre-caches an /offline page and serves it for navigations made without a connection. It explains that file hashing still runs locally while anchoring and verification need the network. A standalone /maintenance page is available for planned downtime and reassures visitors that anchored documents stay safe on the blockchain.
For contributors
A single ErrorPage component backs every one of these pages, so they stay consistent. It takes a code, title, description, optional quick-link suggestions, and an optional inline search box:
import ErrorPage from "@/app/components/ErrorPage";
export default function NotFound() {
return (
<ErrorPage
code="404"
title="Group not found"
description="This group may not exist or may have been created on a different network."
suggestions={[{ label: "Browse all groups", href: "/groups" }]}
/>
);
}