Back to list
oakoss

error-boundaries

by oakoss

Open-source SaaS starter kit with React, TanStack, and Better Auth

0🍴 0📅 Jan 26, 2026

SKILL.md


name: error-boundaries description: React error boundaries + fallback UIs. Use for error, catch, boundary, fallback, recovery, crash, ErrorBoundary, errorComponent, reset

Error Boundaries

Quick Start

TanStack Router provides built-in error handling:

import { Button } from '@oakoss/ui';

export const Route = createFileRoute('/posts/$id')({
  errorComponent: ({ error, reset }) => (
    <div className="p-4">
      <h2>Something went wrong</h2>
      <p>{error.message}</p>
      <Button onPress={reset}>Try again</Button>
    </div>
  ),
});

Error Component Types

LevelComponentUse Case
RouteerrorComponentRoute-specific errors
GlobaldefaultErrorComponentFallback for all routes
Not FoundnotFoundComponent404 errors

Route-Level Error Boundary

import { Button } from '@oakoss/ui';
import { AlertCircle } from 'lucide-react';

export const Route = createFileRoute('/dashboard')({
  component: DashboardComponent,
  errorComponent: DashboardError,
  pendingComponent: DashboardLoading,
});

function DashboardError({ error, reset }: ErrorComponentProps) {
  return (
    <div className="flex flex-col items-center gap-4 p-8">
      <AlertCircle className="size-12 text-destructive" />
      <h2 className="text-lg font-semibold">Failed to load dashboard</h2>
      <p className="text-muted-foreground">{error.message}</p>
      <Button onPress={reset}>Retry</Button>
    </div>
  );
}

Global Error Boundary

Configure in router setup:

// apps/web/src/router.tsx
import { GlobalError } from '@/components/errors/global-error';
import { NotFoundError } from '@/components/errors/not-found-error';

export const getRouter = () =>
  createRouter({
    routeTree,
    defaultErrorComponent: GlobalError,
    defaultNotFoundComponent: NotFoundError,
  });

Error Component Props

type ErrorComponentProps = {
  error: Error; // The caught error
  reset: () => void; // Retry the operation
  info?: { componentStack: string }; // React error info
};

Fallback UI Patterns

Minimal Fallback

function MinimalError({ error, reset }: ErrorComponentProps) {
  return (
    <div className="p-4 text-center">
      <p>Something went wrong.</p>
      <Button variant="link" onPress={reset}>
        Try again
      </Button>
    </div>
  );
}

Detailed Fallback

import { Card, CardHeader, CardContent, CardFooter, Button } from '@oakoss/ui';
import { AlertTriangle } from 'lucide-react';

function DetailedError({ error, reset }: ErrorComponentProps) {
  return (
    <Card className="mx-auto max-w-md">
      <CardHeader>
        <h2 className="flex items-center gap-2 text-lg font-semibold">
          <AlertTriangle className="text-destructive" />
          Error
        </h2>
      </CardHeader>
      <CardContent>
        <p className="text-muted-foreground">{error.message}</p>
        {process.env.NODE_ENV === 'development' && (
          <pre className="mt-4 overflow-auto rounded bg-muted p-2 text-xs">
            {error.stack}
          </pre>
        )}
      </CardContent>
      <CardFooter className="gap-2">
        <Button onPress={reset}>Retry</Button>
        <Button variant="secondary" onPress={() => window.location.reload()}>
          Reload page
        </Button>
      </CardFooter>
    </Card>
  );
}

Not Found Component

import { notFound, Link } from '@tanstack/react-router';
import { Button } from '@oakoss/ui';

export const Route = createFileRoute('/posts/$id')({
  loader: async ({ params }) => {
    const post = await getPost(params.id);
    if (!post) throw notFound();
    return post;
  },
  notFoundComponent: () => (
    <div className="p-8 text-center">
      <h2 className="text-xl font-semibold">Post not found</h2>
      <p className="text-muted-foreground">
        The post you're looking for doesn't exist.
      </p>
      <Button className="mt-4" asChild>
        <Link to="/posts">Back to posts</Link>
      </Button>
    </div>
  ),
});

Triggering Not Found

import { notFound } from '@tanstack/react-router';

// In loader
export const Route = createFileRoute('/posts/$id')({
  loader: async ({ params }) => {
    const post = await getPost(params.id);
    if (!post) throw notFound();
    return post;
  },
});

// In component
function PostContent() {
  const post = Route.useLoaderData();
  if (!post.published) throw notFound();
  return <article>...</article>;
}

Common Mistakes

MistakeCorrect Pattern
Not providing reset functionAlways include retry/reset button
Catching errors in componentLet errors bubble to boundary
Missing route error componentAdd errorComponent to routes
Not logging errorsLog to console and/or external service
Showing stack trace in prodOnly show in NODE_ENV === 'development'
Generic error messageShow context-specific messages
Missing notFoundComponentHandle 404s at route level
Swallowing async errorsUse error boundaries with Suspense
No reload/escape optionProvide "Reload page" fallback button
Not handling loader errorsLoader errors need errorComponent too

Delegation

  • Error logging: For monitoring, consider external services
  • Code review: After creating error components, delegate to code-reviewer agent

Score

Total Score

65/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

Reviews

💬

Reviews coming soon