What is a React error boundary (and why did my page go white)?
In one sentence:An error boundary is a piece of a React app that catches a crash in the components rendered inside it and shows a fallback message instead of leaving the whole page blank.
What it actually is
An error boundary is a piece of your React app that watches everything rendered inside it. If one of those components throws an error while it’s rendering, the error boundary catches that crash, throws away the broken part of the tree, and shows a fallback message in its place.
Think of it like a circuit breaker in a home’s electrical panel. A fault behind one outlet trips the breaker for that circuit, and the rest of the house keeps running. Without any breaker, the same fault can take down the whole house. React works the same way: if no error boundary exists anywhere in your app, an error thrown during rendering unmounts everything below the crash, and with nothing above it to stop that either, the whole page goes blank. Put a boundary around one section instead, and a crash there only takes down that section; the rest of the page keeps working.
Why your AI just did this
Most React starters your agent reaches for don’t include an error boundary out of the box. A Vite React project, the default that Lovable and Bolt scaffold behind the scenes, mounts <App /> with nothing wrapping it, so the first uncaught render crash has nowhere to stop and takes out the entire page. Cursor doesn’t have a fixed default like that; it builds whatever stack you ask for, so the same gap shows up any time you get a plain Vite React app without asking for error handling too.
Next.js’s App Router builds error boundaries into the framework itself through the error.tsx file convention, but a fresh create-next-app project doesn’t ship with one either. Without it, Next.js falls back to its own generic error screen for the whole route. Your agent adds a real, well-placed error boundary when you ask for one, or when it’s copying a template that already has it, not by default, because nothing about a working demo forces the question until something breaks.
When you’ll run into it
The symptom is exactly what it sounds like: the page you were just looking at goes completely blank, no error message and no fallback, just an empty page. Open the browser console and you’ll usually find a stack trace for the error that had nowhere to go.
In a Next.js app with an error.tsx in place, you get a fallback screen instead (“Something went wrong!” is the example text straight from Next.js’s own docs), scoped to just the route segment that crashed. Its sibling file, global-error.tsx, only fires for a crash in the root layout itself, which the regular error.tsx files can’t reach.
What a boundary can’t reach matters just as much. It won’t catch an error thrown inside an onClick handler, a setTimeout callback, or a fetch().then(), because those all run after rendering has already finished. It doesn’t run during server-side rendering either. This is a different failure mode from a hydration error, where the server and the browser render different output for the same page; a boundary can hide that mismatch on screen but doesn’t explain it.
What to check
- Ask your agent point blank whether the app has an error boundary anywhere, a wrapper around the whole app for a Vite or React project, or an
error.tsxfor each major route in a Next.js app - For Next.js, add
app/error.tsxat the root at minimum, plusapp/global-error.tsxto cover a crash in the root layout, which the regular one can’t catch - If you’re on plain React, write a class component with
getDerivedStateFromError, or use thereact-error-boundarypackage so you don’t have to write the class yourself - If a bug slips past a boundary you already have, check whether it’s thrown inside an event handler or async code instead of during rendering; those need their own try/catch and state update, not a boundary
- Test the boundary on purpose: throw a fake error from a component and confirm the fallback shows, rather than discovering during a real edge case that nothing catches it
- Make the fallback message say something true and useful, like “something broke, try again”, instead of a vague apology that hides what happened
Say it like a dev
Instead of: “the whole app just goes white sometimes, no idea why” Say: “A component is throwing during render and nothing catches it. Add an error boundary around the main layout so it shows a fallback instead of blanking the page.”
Instead of: “I added error handling but it still crashes when I click the button” Say: “This error happens inside an onClick handler, not during render, so an error boundary won’t catch it. Wrap it in a try/catch and use state to show the failure instead.”
Instead of: “just wrap everything in try/catch so it never crashes” Say: “Add a root error boundary, plus an error.tsx for each route, instead of scattering try/catch everywhere.”
People actually ask
“Why did my whole app go blank white after one small error?”
Nothing in your component tree caught the crash, so React tore down everything below the failure to avoid showing broken output on screen. A plain React project (a Vite app, for example) with no error boundary anywhere has no safety net, so a crash during rendering unmounts the whole app instead of just the piece that broke. Adding one error boundary around your main layout stops that blast radius at the boundary instead of the whole page.
“How do I add an error boundary to my app?”
In plain React, you write a class component that defines a static getDerivedStateFromError method and wrap it around the part of the tree you want protected, or use the react-error-boundary package instead of writing the class yourself, since React has no function-component version of a boundary. In a Next.js App Router project, add an error.tsx file inside a route folder; Next.js turns it into the boundary for that segment automatically, no wrapping required.
“My error boundary didn't catch this bug, why not?”
Error boundaries only catch errors thrown while React is rendering. An error inside an event handler like onClick, or inside async code like a setTimeout callback or a fetch().then(), happens after rendering has already finished, so a boundary sitting above it never sees it. For those, catch the error yourself with a try/catch, save it into state, and render the fallback in your own code.
Related terms
Checked against
Just met this in a real session? Glim reads it in plain words.
Paste what your agent just did, a git diff, your terminal, or its summary, and Glim tells you what changed and what to check before you ship. Nothing stored.
Explain my session →