React does not allow you to catch render-time errors inside functional components without using a class-based component. This is because:
Only class components can implement componentDidCatch
and getDerivedStateFromError
, which are required to catch rendering errors in React.
Why You Can’t Build a Purely Functional Error Boundary Today
React’s error boundary mechanism is built around lifecycle methods that only exist in class components. As of React 18 and even in experimental React 19, functional components cannot implement componentDidCatch
.
Here’s a summary:
Feature | Functional Components | Class Components |
componentDidCatch() | Not supported | Yes |
getDerivedStateFromError() | Not supported | Yes |
Can catch render errors | No | Yes |
What Can You Do Functionally?
While you can’t catch render-time errors functionally, you can catch event-based or effect-based errors using:
try/catch
inside event handlers- Error management inside
useEffect
ErrorBoundary
wrapper around functional children
Step 1: Create a Class-based ErrorBoundary
import React from 'react'; export class MyErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false, error: null }; } static getDerivedStateFromError(error) { return { hasError: true, error }; } componentDidCatch(error, errorInfo) { if (this.props.onError) { this.props.onError(error, errorInfo); } } reset = () => { this.setState({ hasError: false, error: null }); }; render() { if (this.state.hasError) { const Fallback = this.props.fallback; return ( <Fallback error={this.state.error} resetErrorBoundary={this.reset} /> ); } return this.props.children; } }
Step 2: Create a Fallback UI Component
import React from 'react'; function FallbackUI({ error, resetErrorBoundary }) { return ( <div style={{ padding: 20, border: '1px solid red', backgroundColor: '#fce4e4' }}> <h2>Oops! Something went wrong.</h2> <p style={{ color: 'red' }}>{error.message}</p> <button onClick={resetErrorBoundary}>Try again</button> </div> ); } export default FallbackUI;
Step 3: Create a Custom Hook and Wrapper for Functional Use
import { useCallback, useRef } from 'react'; import { MyErrorBoundary } from './MyErrorBoundary'; /** * This hook is for functional components that want to use imperative error handling. */ export function useCustomErrorBoundary() { const ref = useRef(); const reset = useCallback(() => { ref.current?.reset(); }, []); return { ErrorBoundary: ({ children, fallback, onError }) => ( <MyErrorBoundary ref={ref} fallback={fallback} onError={onError} > {children} </MyErrorBoundary> ), reset, }; }
Step 4: Use It in a Functional Component
import React from 'react'; function BuggyComponent({ shouldCrash }) { if (shouldCrash) { throw new Error('BuggyComponent crashed!'); } return <p>This component is working fine.</p>; } export default BuggyComponent;