Error Handling in React
By Syncfusion®, Inc. · Last updated
React errors, try/catch with async code, API error handling, friendly error messages, error boundaries, fallback UI, debugging and React Developer Tools.
Understanding React Errors
An error is something that stops your code from working as planned. Every app has errors. A good app catches them and tells the user what happened.
In a React app, errors come from two main places:
- Render errors. A component fails while it builds its TSX, for example by reading
.nameofundefined. React cannot draw that part of the page. - Async errors. A request to a server fails, or the server sends back an error code. This happens later, inside an event handler or an effect.
React handles these two kinds in different ways. This lesson shows both.
Real-World Example
In a bookshop app, one book from the server has no author, so BookCard throws an error while it renders. Without protection, React removes the whole page and the user sees a blank screen.
The team wraps each section in an ErrorBoundary. Now only that section shows "Something went wrong", and the rest of the shop keeps working.
Run the lab below to see the problem. BookCard fails, and React removes the whole page, not only that card. The console shows the error.
When a component throws while rendering, React removes the whole tree. It does this on purpose: a broken screen with wrong data is worse than no screen. You fix this with an error boundary, later in this lesson.
Try-Catch with Async Operations
try and catch are plain JavaScript. You put the risky code in try. If any line in it throws an error, JavaScript jumps to catch. finally runs at the end in both cases.
try {
const response = await fetch(url); // may fail
setBooks(await response.json());
} catch (error) {
setError("Could not load books."); // runs only on failure
} finally {
setIsLoading(false); // always runs
}
try runs the risky code. If it fails, catch runs. finally always runs at the end
try fetch data
ok
show the data
fails
catch show a message
finally stop loading
Use try and catch around every await that can fail.
Below, the button calls a server address that does not exist. fetch throws, so catch runs and shows a message. Without try, the error would go to the console and the page would stay on "Loading..." for ever.
In TypeScript, the value in catch (error) has the type unknown, because anything can be thrown. Write (error as Error).message to read its message.
API Error Handling
fetch only throws when the request cannot be sent at all, for example when there is no network. When the server answers with an error code, such as 404 (not found) or 500 (server failed), fetch does not throw. You must check response.ok yourself.
A good pattern is to throw your own error when response.ok is false. Then one catch handles every kind of failure:
if (!response.ok) {
throw new Error("Status " + response.status);
}
User-Friendly Error Messages
"Status 500" means nothing to most users. A good error message says, in plain words:
- What happened: "We could not load the books."
- What to do next: "Please check your internet and try again."
Keep the technical details for the console, where developers can see them. Show the user a short, kind message.
Turn technical error text into a message a user understands
HTTP 500 Internal Server Error
Something went wrong Please try again.
Users need to know what happened and what to do next, not the error code.
A small function can turn a status code into a friendly message. Then every screen uses the same words.
Error Boundaries (Concept)
try and catch cannot catch render errors, because React calls your component, not your code. For render errors, React has error boundaries.
An error boundary is a component that wraps other components. If any component inside it throws while rendering, the boundary catches the error and shows a fallback (a backup screen) instead. The rest of the page keeps working.
Without an error boundary, one broken component blanks the whole page. With one, only that part shows a fallback Without a boundary With a boundary
Header
BookCard (breaks)
Footer
blank page
Header
Sorry, this part failed
Footer
An error boundary keeps the problem inside a small box.
An error boundary must be a class component. This is the one place where React still needs a class. You write it once and reuse it everywhere. Look at the ErrorBoundary.tsx tab:
static getDerivedStateFromError()runs when a child fails. It returns{ hasError: true }, so the next render shows the fallback.componentDidCatch(error, info)also runs. Use it to log the error.render()showsthis.props.fallbackwhenhasErroris true, or the children when all is well.
Now each card is wrapped in its own boundary. Only the broken card shows the fallback.
Error boundaries catch errors while rendering. They do not catch errors in event handlers (like onClick) or in async code (like fetch). Use try and catch for those.
Fallback UI Patterns
The fallback UI is what the user sees when something fails. Some common patterns:
- Small inline message. Replace only the broken part, as with the book card above.
- Section fallback. Wrap a whole area, such as a sidebar, so the rest of the page still works.
- Retry button. Let the user try again. This works well for failed requests.
- Empty state. When the data is just empty, say so ("No books yet") instead of showing an error.
The lab below shows the retry pattern. The first request fails on purpose. Click Try again to load the books.
Debugging Techniques
Debugging means finding out why your code does not work, and fixing it. Some simple habits help a lot:
- Read the error message first. It names the problem and often the file and line. "Cannot read properties of undefined (reading 'name')" means something before
.namewasundefined. - Log values. Put
console.log("book:", book)just before the line that fails. Check that the value is what you expect. - Check the Network tab. In the browser developer tools (press F12), the Network tab shows every request, its status code and its response.
- Use
debugger. Writedebugger;in your code. With the developer tools open, the browser pauses on that line, and you can look at every variable. - Change one thing at a time. Then you know which change fixed the problem.
Easy Way to Remember: read the message, log the value, check the network, change one thing.
React Developer Tools
React Developer Tools is a free browser extension for Chrome, Firefox and Edge. It adds two tabs to the browser's developer tools:
- Components shows your component tree. Click a component to see its props, state and hooks. You can even change a value and watch the page update.
- Profiler records which components render, and how long each render takes. You will use it in the next lesson.
React Developer Tools shows the component tree, and the props and state of the selected component
⚛ Components ⚛ Profiler
▾ App Header ▾ BookList
BookCard BookCard BookCard props title: "Wings of Fire" price: 350 hooks State: false
Click a component on the left to see its props and state on the right.
The playground on this page runs inside a sandbox, so the extension cannot see it. Install the extension, run a React app on your own computer with npm run dev, then press F12 and open the Components tab.
Common mistakes
| Mistake | What you see | Fix |
|---|---|---|
No try/catch around await fetch(...) | Loading never ends, and an error shows in the console | Wrap the request in try/catch, and use finally |
Expecting fetch to throw on 404 or 500 | Error pages are used as data | Check response.ok and throw your own error |
Showing error.message or a status code to users | Users see "Status 500" | Show a short, friendly message. Log the details |
| Writing an error boundary as a function component | It does not catch anything | Use a class with getDerivedStateFromError |
Expecting a boundary to catch onClick or fetch errors | The error is not caught | Use try/catch inside the handler |
| One boundary around the whole app only | Any small error hides the whole page | Also wrap smaller parts, such as each widget |
Check Yourself
Exercises
What to Remember
- Render errors break the component tree. Async errors happen later, in handlers and effects.
- Use
try/catch/finallyaroundawait. Checkresponse.ok, and show short, friendly messages. - An error boundary (a class component) catches render errors in its children and shows a fallback. Debug with the console, the Network tab and React Developer Tools.
Next: Performance Optimization