Place error boundaries at three levels: root, route and widget.
- Root: it catches anything unexpected and shows a full-page fallback with a reload button. So the user never sees a blank screen.
- Route: there is one per page. Navigation and the layout keep working when one page fails.
- Widget: it wraps an independent section, like a recommendations rail, a comments panel or a third-party embed. If that section fails, it shows a small "could not load" card with a retry button. The rest of the page keeps working.
Error boundaries only catch errors during render and in lifecycle methods. They do not catch errors in event handlers or async code. So the data layer must handle data errors too. For example, use query error states, or throw the error to the boundary on purpose.
Graceful degradation means non-critical features fail quietly. Two examples:
- If the recommendations API is down, hide the rail.
- If the chat widget's script is blocked, keep the page usable.
Each boundary reports to error tracking with context. Boundaries reset on a route change or when a retry key changes.
- Use boundaries at three levels: root, route and widget.
- Isolate non-critical and third-party sections in their own boundaries.
- Boundaries do not catch errors in event handlers or async code by themselves.
- Boundaries can retry or reset, and they report to monitoring.