Errors, loading, and recovery
Failure is part of the interface. Put boundaries close enough to the work that one broken region does not erase everything around it.
Give each meaningful route a recovery plan
loading.tsx handles waiting, error.tsx handles unexpected failures in a segment, not-found.tsx handles missing resources, and global-error.tsx is the final boundary around the root.
A recommendations failure should not remove the product title and purchase controls. Boundary placement should follow what can fail independently.
A recoverable error can offer retry
'use client'
export default function Error({ reset }: { reset: () => void }) {
return (
<section role="alert">
<h2>We could not load this section.</h2>
<button onClick={reset}>Try again</button>
</section>
)
}Expected errors are data
Validation failures, unavailable inventory, and rejected permissions are expected outcomes. Return them in a shape the UI understands.
Throw when the current code cannot meaningfully continue, not simply because a result is negative.
Log enough context to debug, not enough to leak
- Include route, operation, correlation/request identifiers, and safe domain identifiers.
- Do not log passwords, tokens, session cookies, raw secrets, or unnecessary personal data.
- Make retries idempotent when the same operation may run more than once.
- Test empty, loading, error, and missing-resource states before production.
Can you explain “Errors, loading, and recovery” without reaching for the API reference?
Describe the main trade-off, one failure mode, and the decision you would make in a real project.