Skip to solution
mediumFrontend

What are Error Boundaries in React?

267 views
01

Understand the problem

Question presented to candidate: "One component throws while rendering and the whole page goes blank. What is the mechanism for handling that?"

What a strong answer should cover:

  • An Error Boundary is a component that catches errors thrown while rendering its subtree, and renders a fallback instead of letting the error unmount the whole tree.
  • Since React 16, an uncaught render error unmounts the entire root — that is the blank page. Boundaries exist to contain the damage.
  • It must be a class component. Two methods: getDerivedStateFromError (return the fallback state — the render-phase half) and componentDidCatch (log it — the commit-phase half, where side effects are allowed).
  • componentDidCatch receives (error, info) where info.componentStack names the component that failed — the single most useful thing to send to your logger.
  • There is no hook version. Libraries wrap a class; the class is still there underneath.
  • Placement is the design decision: one boundary at the root only ever gives you a full-page fallback. Boundaries around independently-failing regions — a widget, a route, a sidebar — keep the rest of the page alive.
  • A boundary needs a way to recover — a retry button, or a key change — or the fallback is permanent.
  • React 19 added root-level onUncaughtError and onCaughtError options for centralised reporting.

Clarifying questions expected:

  • "Which parts of this page should survive if one part fails?" — that determines where boundaries go.
  • "Where do we want these errors reported?"

Code / implementation expected: Optional. The two-method class is short enough to write out.

error handlingerror boundariesuxlifecycle
02

Attempt it yourself

Sketch your approach before reading the solution — that's what interviews test.

Nudge consolestandby

Stuck? Beam a request up — the console returns a conceptual nudge that guides your logic without spoiling the implementation.

03

Study the solution

Target Audience: Engineers preparing for React interviews — no prior error-handling knowledge assumed. Difficulty: Medium

How to read this doc: Concepts are explained in plain language first, then tagged with 📌 Interview term:. The behaviour in section 3 was *executed against React 19.2.8

Solution ready — 2 min read

Classified // press E to declassify

04

Explore the playground snippets

A boundary with retry, and what componentDidCatch actually receives
Run Playground
import React, { Component, useState } from "react";

class ErrorBoundary extends Component {
  state = { error: null, stack: null };

  // RENDER PHASE — must be pure. Return the state that shows the fallback.
  // No logging here: React may render more than once and you would double-report.
  static getDerivedStateFromError(error) {
    return { error };
  }

  // COMMIT PHASE — side effects allowed. This is where reporting belongs.
  // info has exactly one key: componentStack.
  componentDidCatch(error, info) {
    this.setState({ stack: info.componentStack });
    // reportToService(error, info.componentStack)
  }

  render() {
    if (this.state.error) {
      return (
        <div style={{ border: "1px solid #e0b4b4", background: "#fdf0f0", borderRadius: 8, padding: 12 }}>
          <strong style={{ color: "#a33" }}>⚠ {this.props.label} failed</strong>
          <div style={{ fontSize: 13, color: "#a33" }}>{this.state.error.message}</div>
          <pre style={{ fontSize: 11, color: "#666", whiteSpace: "pre-wrap", maxHeight: 90, overflow: "auto" }}>
{String(this.state.stack || "").trim() || "(no component stack)"}
          </pre>
          {/* Without this, the fallback is permanent and the region is dead. */}
          <button onClick={() => this.setState({ error: null, stack: null })}>retry</button>
        </div>
      );
    }
    return this.props.children;
  }
}

function Chart({ broken }) {
  if (broken) throw new Error("Cannot read chart data: series is undefined");
  return <div style={ok}>📈 chart rendering normally</div>;
}

function Sidebar() {
  const [n, setN] = useState(0);
  return (
    <div style={ok}>
      sidebar is unaffected — <button onClick={() => setN((v) => v + 1)}>clicked {n}</button>
    </div>
  );
}

const ok = { border: "1px solid #cde3cd", background: "#f2f9f2", borderRadius: 8, padding: 12 };

export default function App() {
  const [broken, setBroken] = useState(false);

  return (
    <div style={{ padding: 24, fontFamily: "system-ui", lineHeight: 1.6, maxWidth: 540, display: "grid", gap: 10 }}>
      <button onClick={() => setBroken((b) => !b)}>
        {broken ? "fix the chart" : "break the chart"}
      </button>

      {/* Two independent regions, two boundaries. The chart failing does not
          touch the sidebar — that containment is the whole point. */}
      <ErrorBoundary label="Chart">
        <Chart broken={broken} />
      </ErrorBoundary>

      <ErrorBoundary label="Sidebar">
        <Sidebar />
      </ErrorBoundary>

      <p style={{ fontSize: 13, color: "#666" }}>
        Break the chart: its region shows the fallback with the component stack,
        and the sidebar keeps its click count. Press "fix" then "retry" to
        recover. Without the retry button the region would stay dead until a
        reload — a boundary with no exit just converts a crash into a dead end.
      </p>
    </div>
  );
}
05

Join the discussion

Discussion (0)

Sign in to join the discussion.

No responses yet. Be the first to share what you think.

Transmission complete // awaiting log

KEEP THE
STREAK ALIVE.

Dossier 64 of 119 decoded in the React.js track. One more won't hurt.

Back to track