frontend-error-handling
Frontend Error Handling
This skill provides patterns for handling errors in React applications.
Canonical Examples
Study these real implementations:
- API Client Interceptor: client.ts
- Route Error Handler: route-error.tsx
Error Handling Layers
1. API Client Interceptor (Global)
Handle auth errors and network errors globally:
apiClient.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
// Redirect to login
window.location.href = '/login';
}
if (error.response?.status === 403) {
toast.error('You do not have permission to perform this action');
}
if (!error.response) {
toast.error('Network error. Please check your connection.');
}
return Promise.reject(error);
}
);
2. Route-Level Error Handling (TanStack Router — Primary Pattern)
TanStack Router renders the route's errorComponent when a loader or component throws. This is the primary error boundary for route-level failures — use it before reaching for a manual class-based boundary.
// Route definition
export const Route = createFileRoute('/studios/$studioId/tasks')({
component: TasksPage,
errorComponent: RouteError, // <── project-wide shared component
});
The shared RouteError component lives at route-error.tsx.
2b. Error Boundaries (Subtree Fallback)
For subtrees inside a route that need isolated error containment, use the react-error-boundary package:
import { ErrorBoundary } from 'react-error-boundary';
function TaskListSection() {
return (
<ErrorBoundary fallback={<p className="text-destructive">Failed to load tasks.</p>}>
<TaskList />
</ErrorBoundary>
);
}
Avoid writing class-based ErrorBoundary implementations from scratch — react-error-boundary handles the class component requirement internally and is React 19-compatible.
3. TanStack Query Error Handling
Global Mutation Toasts (Centralized via MutationCache):
The project uses a MutationCache onError callback in query-client.ts to automatically toast errors for all mutations.
- Rule: Do NOT implement inline
onError: (error) => toast.error(...)insideuseMutationhooks. Rely on the global handler.
Type-safe meta via Register (TanStack Query v5)
Custom meta fields are declared via module augmentation of the Register interface. This provides IDE autocomplete for meta.suppressErrorToast and meta.errorMessage.
// query-client.ts
declare module '@tanstack/react-query' {
// eslint-disable-next-line -- module augmentation requires interface
interface Register {
defaultError: Error;
mutationMeta: {
suppressErrorToast?: boolean;
errorMessage?: string;
};
}
}
Important: TanStack Query v5 infers
MutationMetafromRegister.mutationMeta. Do NOT declare a separateinterface MutationMeta— it causes a "Duplicate identifier" TypeScript error.
Controlling global toast behavior
// Default: global toast fires automatically on error (no extra code needed)
useMutation({ mutationFn: createTask });
// Suppress the toast entirely (e.g. background autosave)
useMutation({ mutationFn: saveTask, meta: { suppressErrorToast: true } });
// Custom toast message
useMutation({ mutationFn: deleteTask, meta: { errorMessage: 'Failed to delete task' } });
Dynamic suppression for autosave mutations
Mutations that accept a silent flag in their variables automatically suppress the global toast:
// The global MutationCache handler checks variables.silent
mutate({ taskId, data, silent: true }); // ← no toast
mutate({ taskId, data }); // ← toast fires on error
Per-query error handling (component-level)
const { data, error, isError } = useQuery({
queryKey: ['tasks'],
queryFn: fetchTasks,
});
if (isError) {
return <ErrorState message={error.message} />;
}
4. Form Validation Errors
Handle validation errors from Zod:
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
const form = useForm({
resolver: zodResolver(createTaskSchema),
});
// Errors automatically shown via form.formState.errors
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage /> {/* Shows validation error */}
</FormItem>
)}
/>
Error Display Patterns
Toast Notifications (Transient Errors)
import { toast } from 'sonner';
// Success
toast.success('Task created successfully');
// Error
toast.error('Failed to create task');
// Warning
toast.warning('This action cannot be undone');
Inline Error Messages (Form Errors)
{error && <p className="text-sm text-destructive">{error.message}</p>}
Error States (Component-level)
if (isError) {
return (
<div className="flex flex-col items-center justify-center p-8">
<AlertCircle className="h-12 w-12 text-destructive mb-4" />
<h3 className="text-lg font-semibold">Failed to load data</h3>
<p className="text-sm text-muted-foreground">{error.message}</p>
<Button onClick={() => refetch()} className="mt-4">Try again</Button>
</div>
);
}
Best Practices Checklist
- API client has response interceptor for auth/network errors
- Error boundaries wrap route components
- TanStack Query has global error handlers
- Form validation uses Zod with react-hook-form
- Toast notifications for transient errors
- Inline error messages for form fields
- Error states for failed queries
- Retry buttons for recoverable errors
- User-friendly error messages (no stack traces)
Related Skills
- frontend-api-layer - API client setup
- data-validation - Validation patterns
More from allenlin90/eridu-services
service-pattern-nestjs
Comprehensive NestJS service implementation patterns. This skill should be used when implementing Model Services, Orchestration Services, or business logic with NestJS decorators.
8erify-authorization
Patterns for implementing authorization in erify_api with current StudioMembership + AdminGuard behavior, plus planned RBAC references
6data-validation
Provides comprehensive guidance for input validation, data serialization, and ID management in backend APIs. This skill should be used when designing validation schemas, transforming request/response data, mapping database IDs to external identifiers, and ensuring type safety across API boundaries.
6code-quality
Provides general code quality and best practices guidance applicable across languages and frameworks. Focuses on linting, testing, and type safety.
6repository-pattern-nestjs
Comprehensive Prisma repository implementation patterns for NestJS. This skill should be used when implementing repositories that extend BaseRepository or use Prisma delegates.
6task-template-builder
Provides guidelines for the Task Template Builder architecture, including Schema alignment, Draft storage, Drag-and-Drop, and Validation logic.
6