Back to React
React
hard
mid

What kind of fallback UI would you design for different error types such as 404, 500, and network errors?

Match the fallback to the error: 404 = friendly 'not found' with search + popular links; 500 = apologetic error with retry and a status link; network = inline retry with cached content if available; auth (401/403) = redirect to login / show 'no access'; chunk-load failure = soft reload prompt. Always: clear copy, a next action, no stack traces, log to monitoring.

4 min read·~15 min to think through

A good fallback UI gives the user a path forward, never just "Error". The right design depends on the error class.

1. 404 — Not Found

The user navigated to something that doesn't exist. Friendly tone; offer recovery.

  • Heading: "We couldn't find that page."
  • Search bar + links to popular destinations / home.
  • No stack trace, no apology spiral.
  • Log so you can detect broken links (especially internal).

2. 500 / 5xx — Server Error

Something broke server-side; the user can't fix it. Apologize briefly; offer retry and status.

  • "Something went wrong on our end."
  • "Try again" button (with backoff).
  • Link to status page if you have one.
  • Capture the error id / trace id; show it so support can find it: "Reference: ab123…"
  • Log to Sentry/Datadog with full context.

3. Network failure

The request never reached the server (or no response). Inline retry; show cached content if available.

  • Toast or inline banner: "You're offline / Couldn't reach the server. Retry."
  • Don't blow away the page — preserve scroll, form input.
  • If a service worker has cached data, render that with a "stale" indicator.

4. Auth — 401 / 403

  • 401 (not authenticated) — redirect to login, preserving the destination URL.
  • 403 (no access) — "You don't have access to this resource" with a contact link / request access if applicable. Don't hint at whether the resource exists (security).

5. Chunk-load failure (code-split bundle)

A deploy invalidated a chunk URL the user's tab was about to fetch. The user sees a JS error and a blank screen — terrible UX.

  • Catch in an error boundary; detect by error name (ChunkLoadError).
  • Show a soft "We've updated this page. Reload to continue." prompt with a Reload button.
  • Optionally auto-reload after a few seconds.

6. Validation / 400 errors

Inline next to the offending field. Never a global "Error" banner for a field error.

7. Empty states (not strictly errors)

Distinguish: "No results match these filters" is different from "Failed to load results". Both deserve their own design.

8. Patterns that apply across all

  • Clear copy — no stack traces, no jargon, no "Whoops!".
  • One primary action — Retry, Go Home, Login.
  • Preserve context — don't unmount the whole app; scope the error boundary tight.
  • Log to monitoring with breadcrumbs, user id (non-PII), and route.
  • Accessibilityrole="alert" for important errors; don't hide errors visually only.
  • Don't auto-clear errors that need user action.

9. Architecture

  • Route-level error boundaries for 404/500/auth.
  • Component-level error boundaries around risky widgets (charts, embeds) so one widget failure doesn't kill the page.
  • Network errors handled in the data layer (React Query onError, Axios interceptor).
  • Chunk-load handled at the dynamic-import call site.

Interview framing

"Match the fallback to the error class. 404 is a navigation problem — friendly, with search and links. 500 is server-side — apologize, offer retry, surface a trace id for support. Network failure deserves inline retry with cached content if we have it. Auth 401 redirects to login preserving the destination; 403 is 'no access' without leaking existence. Chunk-load failures need a soft reload prompt — they happen after deploys. The shared rules: clear copy, one primary action, scoped error boundaries so one failure doesn't kill the app, and log everything to monitoring with enough context to debug."

Follow-up questions

  • Why distinguish 401 from 403 in the UI?
  • How do you handle chunk-load errors after a deploy?
  • Why scope error boundaries narrowly?
  • What goes into the error log to make it actionable?

Common mistakes

  • One generic 'Something went wrong' for every error class.
  • Showing a stack trace or 'undefined is not a function' to a user.
  • Unmounting the whole app on a widget error.
  • 404 with no path forward.
  • Auto-clearing errors that need user action.

Performance considerations

  • Error boundaries shouldn't ship heavy code in the bundle — lazy-load the fallback's media/illustrations.

Edge cases

  • Offline → online recovery.
  • Login session expires mid-action.
  • Stale cached chunks after deploy.
  • Same error class for different root causes (timeout vs 500).

Real-world examples

  • GitHub's 404 octocat, Stripe's retry-friendly error toasts, Linear's auth-required redirect.

Senior engineer discussion

Seniors treat error UX as a design surface, scope boundaries narrowly to contain blast radius, log with enough context to debug, and handle chunk-load failures explicitly — they're the most common 'why is the page blank' bug after a deploy.

Related questions