Error Boundary
Components and hooks for error handling.
Import
ts
import {
ErrorBoundary,
RouteErrorBoundary,
useErrorBoundary,
useRouteError,
isRouteErrorResponse,
createRouteErrorResponse,
withErrorBoundary,
RouteError
} from '@ereo/client'ErrorBoundary
A React error boundary component that catches JavaScript errors in child components.
Props
ts
interface ErrorBoundaryProps {
children: ReactNode
// Fallback UI when an error occurs
// Can be a static ReactNode or a function receiving error and reset function
fallback?: ReactNode | ((error: Error, reset: () => void) => ReactNode)
// Callback when an error is caught
onError?: (error: Error, errorInfo: ErrorInfo) => void
}Basic Usage
tsx
import { ErrorBoundary } from '@ereo/client'
function App() {
return (
<ErrorBoundary fallback={<div>Something went wrong</div>}>
<MyComponent />
</ErrorBoundary>
)
}With Error Callback
tsx
<ErrorBoundary
fallback={<ErrorMessage />}
onError={(error, errorInfo) => {
// Log to error tracking service
Sentry.captureException(error, { extra: errorInfo })
}}
>
<MyComponent />
</ErrorBoundary>Dynamic Fallback with Reset
tsx
<ErrorBoundary
fallback={(error, reset) => (
<div>
<h1>Error</h1>
<p>{error.message}</p>
<button onClick={reset}>
Try Again
</button>
</div>
)}
>
<MyComponent />
</ErrorBoundary>RouteErrorBoundary
An error boundary specifically for route-level error handling. Integrates with route configuration and supports error recovery strategies.
Props
ts
interface RouteErrorBoundaryProps {
// Route identifier
routeId: string
// Error configuration from route
errorConfig?: ErrorConfig
// Children to render
children: ReactNode
// Fallback component from route module
fallbackComponent?: ComponentType<{ error: Error; params: RouteParams }>
// Route params
params?: RouteParams
}Usage
tsx
// routes/_error.tsx
import { RouteErrorBoundary, useRouteError, isRouteErrorResponse } from '@ereo/client'
export function ErrorBoundary() {
const error = useRouteError()
if (isRouteErrorResponse(error)) {
return (
<div>
<h1>{error.status}</h1>
<p>{error.statusText}</p>
</div>
)
}
return (
<div>
<h1>Error</h1>
<p>{error?.message || 'Unknown error'}</p>
</div>
)
}useRouteError
Hook to access the error in an error boundary.
Signature
ts
function useRouteError(): RouteErrorResponse | Error | unknownExample
tsx
function ErrorBoundary() {
const error = useRouteError()
// Handle different error types
if (isRouteErrorResponse(error)) {
switch (error.status) {
case 404:
return <NotFoundPage />
case 401:
return <UnauthorizedPage />
case 500:
return <ServerErrorPage />
default:
return <GenericErrorPage status={error.status} />
}
}
if (error instanceof Error) {
return <ErrorPage message={error.message} />
}
return <ErrorPage message="An unknown error occurred" />
}isRouteErrorResponse
Type guard for route error responses.
Signature
ts
function isRouteErrorResponse(value: unknown): value is RouteErrorResponse
interface RouteErrorResponse {
status: number
statusText: string
data: any
}Example
tsx
const error = useRouteError()
if (isRouteErrorResponse(error)) {
// error.status, error.statusText, error.data are available
console.log(error.status) // 404
}createRouteErrorResponse
Creates a route error response.
Signature
ts
function createRouteErrorResponse(
status: number,
statusText: string,
data?: any
): RouteErrorResponseExample
tsx
// In a loader
export const loader = createLoader(async ({ params }) => {
const post = await db.posts.find(params.id)
if (!post) {
throw createRouteErrorResponse(404, 'Not Found', {
message: `Post ${params.id} not found`
})
}
return { post }
})useErrorBoundary
Hook for programmatic error boundary control. Provides access to error state and functions to reset or trigger errors.
Signature
ts
function useErrorBoundary(): UseErrorBoundaryReturn
interface UseErrorBoundaryReturn {
// Current error (undefined if no error)
error: Error | undefined
// Reset the error boundary
reset: () => void
// Programmatically trigger an error in the boundary
showBoundary: (error: Error) => void
}Example
tsx
function DataLoader() {
const { showBoundary } = useErrorBoundary()
const loadData = async () => {
try {
const data = await fetchData()
return data
} catch (error) {
showBoundary(error)
}
}
return <button onClick={loadData}>Load</button>
}Reset Boundary
tsx
function ErrorFallback() {
const { error, reset } = useErrorBoundary()
return (
<div>
<h1>Error</h1>
<p>{error?.message}</p>
<button onClick={reset}>Try Again</button>
</div>
)
}withErrorBoundary
HOC to wrap a component with an error boundary.
Signature
ts
function withErrorBoundary<P>(
Component: ComponentType<P>,
options?: ErrorBoundaryOptions
): ComponentType<P>Example
tsx
const SafeComponent = withErrorBoundary(RiskyComponent, {
fallback: <div>Error loading component</div>,
onError: (error) => console.error(error)
})
// Use like normal component
<SafeComponent prop="value" />RouteError
Custom error class for route errors with HTTP status codes.
Constructor
ts
class RouteError extends Error {
status: number
statusText: string
data: unknown
constructor(status: number, statusText: string, data?: unknown)
// Convert to RouteErrorResponse
toResponse(): RouteErrorResponse
}Example
tsx
import { RouteError } from '@ereo/client'
// In a loader
export async function loader({ params }) {
const post = await db.posts.find(params.id)
if (!post) {
throw new RouteError(404, 'Not Found', { message: 'Post not found' })
}
if (!post.published) {
throw new RouteError(403, 'Forbidden', { postId: params.id })
}
return { post }
}
// Converting to response
const error = new RouteError(404, 'Not Found')
const response = error.toResponse()
// { status: 404, statusText: 'Not Found', data: undefined }Error Boundary Patterns
Nested Error Boundaries
tsx
function App() {
return (
<ErrorBoundary fallback={<AppError />}>
<Layout>
<ErrorBoundary fallback={<SidebarError />}>
<Sidebar />
</ErrorBoundary>
<ErrorBoundary fallback={<ContentError />}>
<Content />
</ErrorBoundary>
</Layout>
</ErrorBoundary>
)
}Retry on Error
tsx
function RetryableComponent({ children }) {
const [key, setKey] = useState(0)
return (
<ErrorBoundary
key={key}
fallback={(error) => (
<div>
<p>{error.message}</p>
<button onClick={() => setKey(k => k + 1)}>
Retry
</button>
</div>
)}
>
{children}
</ErrorBoundary>
)
}Error Logging
tsx
function App() {
const logError = (error: Error, errorInfo: React.ErrorInfo) => {
// Log to service
fetch('/api/log-error', {
method: 'POST',
body: JSON.stringify({
error: error.message,
stack: error.stack,
componentStack: errorInfo.componentStack
})
})
}
return (
<ErrorBoundary
fallback={<ErrorPage />}
onError={logError}
>
<App />
</ErrorBoundary>
)
}Route Error Page
tsx
// routes/_error.tsx
export function ErrorBoundary() {
const error = useRouteError()
if (isRouteErrorResponse(error)) {
return (
<div className="error-page">
<h1>{error.status}</h1>
<p>{error.statusText}</p>
{error.status === 404 && (
<Link href="/">Go Home</Link>
)}
</div>
)
}
return (
<div className="error-page">
<h1>Oops!</h1>
<p>Something went wrong.</p>
<button onClick={() => window.location.reload()}>
Reload Page
</button>
</div>
)
}