React Error Boundaries with Axios: Best Practices

Handling asynchronous API failures in React requires a coordinated strategy because native React Error Boundaries do not catch asynchronous errors out of the box. Combining centralized Axios interceptors with React Error Boundaries creates a resilient architecture that captures network failures, normalizes error responses, and displays appropriate fallback interfaces. This guide outlines best practices for bridging Axios and React Error Boundaries to build a predictable, scalable error-handling pipeline.

1. Bridge the Async Gap with React Error Boundaries

React Error Boundaries only catch errors thrown during rendering, in lifecycle methods, and in constructors. They do not catch errors inside asynchronous functions, such as an Axios get or post request.

To propagate an Axios error to the nearest Error Boundary, re-throw the error into React's render cycle using a state updater function or a utility library like react-error-boundary:

import { useState, useCallback } from 'react';

export const useAsyncError = () => {
  const [, setError] = useState();
  return useCallback((e) => {
    setError(() => {
      throw e;
    });
  }, []);
};

Using the react-error-boundary package provides the useErrorBoundary hook, which allows you to call showBoundary(error) directly inside a .catch() block or a React Query/SWR error callback.

2. Centralize Error Normalization via Axios Interceptors

Do not handle raw HTTP error payloads inside individual UI components. Use Axios response interceptors to transform diverse backend responses into standardized error objects.

import axios from 'axios';

const apiClient = axios.create({
  baseURL: 'https://api.example.com',
});

apiClient.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 401) {
      // Handle authentication refresh or redirection
      return Promise.reject(new AuthError('Session expired'));
    }

    const normalizedError = {
      message: error.response?.data?.message || 'An unexpected error occurred.',
      status: error.response?.status || 500,
      isNetworkError: !error.response,
    };

    return Promise.reject(normalizedError);
  }
);

3. Implement Layered Error Boundaries

Do not rely on a single root-level Error Boundary. Structure boundaries hierarchically across three primary tiers:

4. Provide Reset and Retry Mechanisms

A robust error boundary should allow the user to recover without forcing a full page reload.

5. Log Errors for Observability

Use the componentDidCatch lifecycle method or the onError prop of your boundary to report network and rendering failures to external monitoring services (such as Sentry or Datadog). Attach the normalized Axios error details—such as HTTP status codes and endpoint URLs—while stripping sensitive information like authentication tokens and personally identifiable information (PII).