Error handling
Choose route and global error views, model normal failures as state, and contain exceptions at the nearest useful boundary.
Return expected failures explicitly
Validation failures, empty results, rejected permissions, and unavailable remote services can be normal application outcomes. Give them typed data or response shapes so the page can explain what happened and offer a useful next action.
Associate form errors with the field that needs attention.
Use status codes and JSON shapes consistently across route actions and server routes.
Call
notFound()when missing route data should render404.tsx; unmatched URLs use the same page.
Contain unexpected render failures
ErrorBoundary protects a component subtree and renders fallback UI when rendering fails. Place boundaries around areas that can recover independently instead of replacing the entire application for one failed panel.
Create src/components/ProjectPanel.tsx
tsximport { ErrorBoundary, type Child } from "@tavojs/core";
import { Button, Stack, Text } from "@tavojs/ui";
export function ProjectPanel({ children }: { children?: Child }) {
return (
<ErrorBoundary
resetKey="projects"
fallback={(error) => (
<Stack gap="sm">
<Text role="alert">Projects could not be displayed.</Text>
<Button onClick={() => console.error(error)}>Report problem</Button>
</Stack>
)}
>
{children}
</ErrorBoundary>
);
}
Choose the closest route error view
A page can export an error component for its own loader failures. It receives PageErrorProps, including the target pathname, params, data and error fields, resolved layers, and layer data, and it renders inside the matched layout chain.
If the target page does not export error, Tavo.js uses src/pages/_error.tsx as the application-wide fallback. A layout-loader failure enters this error-resolution path instead of showing the page pending component with invalid layout data.
Use a page
errorexport when the recovery message or next action is specific to that route.Keep
_error.tsxbroad enough to handle any remaining page or layout loader failure.notFound()bypasses both route and global error views and renders404.tsxwith status 404.Render failures belong in the nearest component
ErrorBoundarybecause they happen after route resolution.
Design asynchronous failure states
Resources expose idle, loading, success, and error states. Deferred boundaries accept error and timeout fallbacks. A retry should start a new owned operation and replace the old signal rather than reusing abandoned work.
Do not report deliberate cancellation as an error.
Set timeouts for optional dependencies that should not hold a stream open.
Instrument the route, loader, action, cache, or boundary phase without recording private payloads.