Menu

Error boundary en React: captura errores con un fallback

Un error boundary es un componente que captura los errores lanzados al renderizar los componentes que contiene y muestra un fallback en lugar de una pantalla en blanco. Aprende qué captura, cómo escribir uno como clase, cómo reiniciarlo y la librería react-error-boundary.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

Un error boundary es un componente de React que captura los errores lanzados al renderizar los componentes que contiene y muestra una UI de respaldo en lugar de romper toda la app. Tiene que ser un componente de clase que defina static getDerivedStateFromError, porque no hay un hook para esto. Lo escribes una vez y lo pones alrededor de cualquier parte del árbol.

Pulsa "Break the profile" para que el hijo lance un error durante el renderizado.

El perfil se reemplaza por el fallback, y el resto de la página sigue funcionando. La consola muestra el error dos veces: React registra cada error capturado con console.error, y componentDidCatch lo vuelve a registrar. "Try again" elimina la causa (broken) y el error del boundary, así que el perfil se vuelve a renderizar. Quita this.props.onReset(); de reset y Try again vuelve directamente al fallback, porque nada corrigió la causa.

Cómo funciona la clase

Dos métodos convierten una clase en un error boundary:

  • static getDerivedStateFromError(error) se ejecuta durante el renderizado, justo después de que un hijo lanza un error. Devuelve la actualización de estado que pone el boundary en su fallback. Debe ser pura: nada de registros ni peticiones.
  • componentDidCatch(error, info) se ejecuta cuando el fallback ya está en pantalla. Úsalo para efectos secundarios como enviar el error a un servicio de reportes. info.componentStack enumera el componente que lanzó el error y los componentes que tiene encima.
class ErrorBoundary extends Component {
    state = { error: null };

    static getDerivedStateFromError(error) {
        return { error }; // switch to the fallback
    }

    componentDidCatch(error, info) {
        reportError(error, info.componentStack); // your logging service
    }

    render() {
        if (this.state.error) return this.props.fallback;
        return this.props.children;
    }
}

No hay un equivalente en hooks, así que incluso una app escrita solo con componentes de función necesita esta única clase. La página de componentes de clase explica la sintaxis de clases si es nueva para ti.

Qué captura un boundary y qué no

Un boundary captura los errores que ocurren mientras React trabaja en los componentes que tiene dentro:

  • el renderizado (el cuerpo de un componente de función, el render de una clase),
  • los métodos del ciclo de vida y los constructores de los componentes de clase hijos,
  • los efectos (useEffect, useLayoutEffect) y las ref callbacks,
  • una promesa rechazada leída con use, y en React 19 un error lanzado por una acción de formulario o por una función pasada al startTransition de useTransition.

No captura:

  • errores en manejadores de eventos,
  • código async que inicias tú: setTimeout, callbacks .then(), un await en un manejador de eventos,
  • errores en el propio render del boundary (los captura el siguiente boundary hacia arriba),
  • errores durante el renderizado en el servidor.

Los manejadores de eventos quedan fuera porque no rompen el renderizado. La pantalla sigue en un estado válido, así que un try/catch en el manejador basta. Si sí quieres que se muestre el boundary, guarda el error en el estado y lánzalo en el siguiente renderizado:

El primer botón deja los botones en pantalla y solo imprime un error no capturado en la consola. El segundo captura el rechazo, lo guarda y el boundary toma el control.

Dónde poner los boundaries

Un boundary alrededor de toda la app evita la pantalla en blanco, pero entonces cualquier error pequeño lo reemplaza todo. Pon boundaries alrededor de las partes que pueden fallar de forma independiente: un widget de la barra lateral, un gráfico, cada ruta. Así un fallo le cuesta al usuario un panel, no la página.

Solo el panel Stocks muestra su fallback. Mueve ambos widgets dentro de un solo <ErrorBoundary> y el panel Weather, que funciona, desaparece con él.

Sin ningún boundary, React quita todo el árbol de la pantalla cuando un error de renderizado no se captura. Una página vacía es más segura que una rota que muestra datos incorrectos, pero los usuarios la perciben como un fallo.

La librería react-error-boundary

El paquete react-error-boundary envuelve la clase por ti y agrega utilidades para reiniciar. No está disponible en este editor, así que aquí está en un bloque estático:

import { ErrorBoundary, useErrorBoundary } from 'react-error-boundary';

function Fallback({ error, resetErrorBoundary }) {
    return (
        <div role="alert">
            <p>Something went wrong: {error.message}</p>
            <button onClick={resetErrorBoundary}>Try again</button>
        </div>
    );
}

<ErrorBoundary FallbackComponent={Fallback} onReset={() => refetch()} resetKeys={[userId]}>
    <Profile userId={userId} />
</ErrorBoundary>;

// Inside a child: hand an async error to the boundary
const { showBoundary } = useErrorBoundary();
fetchData().catch(showBoundary);

resetKeys reinicia el boundary cuando cambia cualquiera de los valores listados, y showBoundary hace el truco de "guardar y lanzar" del ejemplo anterior.

Reportar errores en React 19

React 19 reporta los errores de una forma nueva. Los errores capturados por un boundary se registran una vez con console.error, y los no capturados van a window.reportError. Para enviarlos a tu propio servicio, pasa opciones a createRoot. Cada opción reemplaza el registro por defecto correspondiente:

import { createRoot } from 'react-dom/client';

const root = createRoot(document.getElementById('root'), {
    onCaughtError(error, errorInfo) {
        // an error boundary caught it
        logToService('caught', error, errorInfo.componentStack);
    },
    onUncaughtError(error, errorInfo) {
        // no boundary caught it, and React removed the tree
        logToService('uncaught', error, errorInfo.componentStack);
    },
    onRecoverableError(error, errorInfo) {
        // React recovered by itself, for example after a hydration mismatch
        logToService('recovered', error, errorInfo.componentStack);
    },
});
root.render(<App />);

onRecoverableError es para errores que React corrigió sin tu ayuda, como un desajuste de hidratación o un error de renderizado que desapareció cuando React reintentó. hydrateRoot acepta las mismas tres opciones.

Errores comunes

Poner el boundary dentro del componente que lanza el error. Un boundary solo captura los errores de sus hijos. Si Profile renderiza <ErrorBoundary> alrededor de su propio JSX y luego lanza un error en su propio cuerpo, el error pasa de largo hasta el siguiente boundary hacia arriba. Envuelve el componente desde su padre.

Efectos secundarios en getDerivedStateFromError. Se ejecuta durante el renderizado y puede ejecutarse más de una vez, así que limítate a devolver estado. Los registros van en componentDidCatch o en el onCaughtError de la raíz.

Un reinicio que no corrige nada. Limpiar el error del boundary renderiza los mismos hijos con las mismas props. Si vuelven a lanzar el error, el usuario vuelve al fallback. Cambia primero lo que causó el error (volver a pedir los datos, limpiar la entrada inválida, cambiar la key) y luego limpia el error, como hace el primer ejemplo con onReset.

Un solo boundary para toda la app, y nada más. Evita la página en blanco pero convierte cada fallo pequeño en un error de página completa. Agrega boundaries alrededor de las funcionalidades, y deja el de nivel superior como último recurso.

Fallbacks que te ocultan el error. Un mensaje amable es lo correcto para los usuarios, pero asegúrate de que el error siga llegando a tus registros. React 19 registra por defecto los errores capturados en la consola; en producción esa consola es la de tu usuario, así que envía los errores a un servicio desde componentDidCatch u onCaughtError.

Los error boundaries combinan de forma natural con <Suspense>: Suspense maneja "todavía no está listo", el boundary maneja "falló". Pon el boundary fuera del Suspense, como en los ejemplos de use, para que un solo componente cubra la carga, el éxito y el fallo.

Preguntas frecuentes

¿Qué es un error boundary en React?

Un componente que captura los errores de JavaScript lanzados al renderizar cualquier componente que contiene, y renderiza en su lugar una UI de respaldo. Sin uno, un error durante el renderizado quita toda la app de la pantalla.

¿Hay un hook para los error boundaries?

No. Un error boundary debe ser un componente de clase con static getDerivedStateFromError (y opcionalmente componentDidCatch). Lo escribes una vez y lo usas desde componentes de función como cualquier otro componente, o instalas react-error-boundary, que envuelve la clase por ti.

¿Los error boundaries capturan errores en los manejadores de eventos?

No. Los manejadores de eventos se ejecutan fuera del renderizado, así que React no necesita un boundary para recuperarse. Usa try/catch en el manejador, y si quieres que se muestre el boundary, guarda el error en el estado y lánzalo durante el renderizado.

¿Los error boundaries capturan errores async?

No los de setTimeout, callbacks de promesas o fetch().then() que inicies tú. Sí capturan una promesa rechazada leída con use, y en React 19 un error lanzado dentro de una acción de formulario o de una función pasada al startTransition de useTransition.

¿Cómo reinicio un error boundary?

Limpia su estado de error, normalmente desde un botón Try again en el fallback, después de corregir lo que causó el error. Cambiar la key del boundary también lo reinicia, porque React monta una instancia nueva.

Ilustración de los lenguajes de programación de Coddy

Aprende a programar con Coddy

COMENZAR