Contain a broken UI region and offer a deliberate retry.
Read the explanation, run the worked code, then complete the practice lab in Assignments. This advanced lesson includes worked code and a dedicated practice lab.
Understand the idea
An error boundary renders an alternative UI when a descendant throws during rendering or certain lifecycle work. A boundary around a dashboard panel can preserve the surrounding navigation. The classic React API uses a class with getDerivedStateFromError and optionally componentDidCatch; a function component cannot implement that boundary behavior just by wrapping its JSX in try/catch.
Place boundaries according to the experience you want to preserve. One boundary for the entire application is simple but can hide everything. A boundary per meaningful panel isolates failures without covering the page in unrelated retry controls. Log useful diagnostic information through an appropriate reporting service, keeping private form data out of reports.
Ordinary event-handler exceptions, rejected requests and server-rendering errors are not handled by a client error boundary in the same way. Catch asynchronous failures where they occur and represent expected errors in state. Retry must change the condition that caused the failure or reinitialize the affected region; simply clearing the error flag while leaving broken props unchanged immediately fails again.
Run the example
Use a separate React practice project with a Node.js version supported by Vite. Create it once with these commands, then use its development URL. Do not paste this into the Edupoly website’s own source files.
npm create vite@latest react-practice -- --template react
cd react-practice
npm install
npm run dev
Replace src/App.jsx with the following complete example. Keep Vite’s generated src/main.jsx entry point.
src/App.jsx
import { Component, useState } from 'react';
class PanelBoundary extends Component {
state = {failed: false};
static getDerivedStateFromError() { return {failed: true}; }
componentDidCatch(error, info) { console.error('Panel failed', error.message, info.componentStack); }
render() {
return this.state.failed ? <section role="alert"><p>The report could not be displayed.</p><button onClick={this.props.onRetry}>Retry report</button></section> : this.props.children;
}
}
function Report({broken}) {
if (broken) throw new Error('Practice report failure');
return <p>Report loaded successfully.</p>;
}
export default function App() {
const [broken, setBroken] = useState(false); const [version, setVersion] = useState(0);
function retry() {setBroken(false); setVersion(n => n + 1);}
return <main><h1>Dashboard navigation stays available</h1><button onClick={() => setBroken(true)}>Break report</button>
<PanelBoundary key={version} onRetry={retry}><Report broken={broken} /></PanelBoundary>
</main>;
}
Expected result
Break report replaces only the report region with a recovery message. Retry restores it. A development error overlay or console error may also appear for the intentional failure.
Apply this to your assignments
Add a second healthy panel and verify it survives the first panel’s failure. Test Retry twice. Separately simulate a rejected fetch and handle it with request-error state; explain why it is not the same as a render exception.
Practice starting point
Use the worked example above as your starting point.
Open Assignments for this lesson’s practice lab and acceptance checks.
Common mistakes
A boundary cannot catch its own failure. Do not treat validation errors as crashes. Reset the failing data or identity before retrying.
Check your understanding
- Predict the output before running the example, then compare it with the result panel.
- Explain which value each expression reads and which action changes it.
- Try the boundary cases listed in the practice task and record what you observe.
- Rebuild one matching assignment without copying, then compare behavior with the original example.
Reference
Official documentation for this topic · Vite setup requirements
