How to Display HTTP Status Codes for Fetch Errors in ReactJS

I’m working on a ReactJS component where I need to fetch data from my backend and update the component’s state. My goal is to handle errors properly when making network calls to an Express server, so I can pass the state down to other child components reliably. I tried forcing an error by fetching from a non-existent endpoint, expecting a 404 error, but instead, I’m getting a syntax error (“Unexpected token < in JSON at position 0”). I’m trying to figure out how to surface the HTTP status code correctly in my catch statement so I can handle errors more effectively.

Error Code:

codeexport default class App extends Component {

constructor(props) {
super(props);
this.state = {
data: null
};
}

componentDidMount() {
fetch('/api/wrong_endpoint').then((data) => {
return data.json();
}).then((body) => {
this.setState({data: body})
}).catch(err => console.log(err));

}

render() {
console.log('logging the states');
console.log(this.state.data);
return (
<div>
<ContactList />
<ContactDetail />
<AddContactModal />
</div>
);
}
}

Soluction:

The issue here is that when the fetch call encounters a 404 error, it tries to parse the response as JSON, which causes a SyntaxError because the response is typically an HTML error page, not JSON. To solve this, we can check if the response status is OK (status 200) before attempting to parse it as JSON. If the response has an error status (e.g., 404), we can throw an error with the status code, which we can catch and handle appropriately.

Correct Code:

codeexport default class App extends Component {

constructor(props) {
super(props);
this.state = {
data: null,
error: null
};
}

componentDidMount() {
fetch('/api/wrong_endpoint')
.then((response) => {
// Check if the response is OK (status 200-299)
if (!response.ok) {
// Throw an error with the status text to be caught below
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then((body) => {
this.setState({ data: body });
})
.catch((err) => {
// Set the error in state to handle or display it
console.error("Fetch error: ", err);
this.setState({ error: err.message });
});
}

render() {
console.log('logging the states');
console.log(this.state.data);

return (
<div>
{this.state.error ? (
<p>Error: {this.state.error}</p>
) : (
<>
<ContactList />
<ContactDetail />
<AddContactModal />
</>
)}
</div>
);
}
}

Explanation:

  1. Status Check: Inside the then block after fetch, we check if response.ok is true (i.e., the status is between 200-299). If not, we throw a new error with the response status code. This allows us to bypass parsing as JSON if there’s an HTTP error.
  2. Error Handling in catch: If an error occurs (either from a failed fetch or the forced error with throw), it’s caught in the catch block. Here, we log the error and set it in the component’s state, allowing us to display it in the UI.
  3. Conditional Rendering: We use a conditional to render an error message if there’s an error, or the normal component structure if there’s no error. This way, the user can see what went wrong instead of the app failing silently.

This approach ensures that you handle HTTP errors gracefully, showing relevant messages without crashing due to parsing issues.

Related blog posts