MyApp

Getting Started

Introduction

User Interface

Button.tsxCard.tsxDialog.tsxMarquee.tsxBentoGrid.tsx

Feature Components

PricingCard.tsxTestimonials.tsxTotalBuyers.tsxFaq.tsxEvent.tsx

Utility Components

SvgFinder.tsxCustomIcons.tsImageWithFallback.tsx

Patterns

Layout PatternsForm PatternsData Fetching PatternsError Handling Patterns
MyApp

Error Handling Patterns

Error handling strategies for forms, API routes, server components, and client interactions.

Error handling in Plainform uses Next.js error boundaries, toast notifications, form validation, and API error responses.

Form Validation Errors

Zod Schema Errors

Field Validation
import { z } from 'zod';

const schema = z.object({
  email: z.string().email('Invalid email address'),
  password: z.string().min(8, 'Must be at least 8 characters'),
});

<Input
  {...register('email')}
  errorMessage={errors.email?.message}
  isInvalid={!!errors.email}
/>

Manual Field Errors

Set Error from API
catch (err: any) {
  setError('email', {
    type: 'manual',
    message: 'This email is already registered',
  });
}

API Error Handling

Clerk API Errors

Clerk Error Pattern
import { ClerkAPIError } from '@clerk/types';
import { toast } from 'sonner';

catch (err: any) {
  err.errors.forEach((error: ClerkAPIError) => {
    const field = error.meta?.paramName as keyof FormData;
    
    if (field && error.longMessage) {
      setError(field, { message: error.longMessage });
    } else {
      toast.error(error.message);
    }
  });
}

Stripe API Errors

Stripe Error Handling
import Stripe from 'stripe';

catch (error) {
  if (error instanceof Stripe.errors.StripeCardError) {
    return { error: 'Card was declined' };
  } else if (error instanceof Stripe.errors.StripeRateLimitError) {
    return { error: 'Too many requests' };
  }
  return { error: 'Payment failed' };
}

API Route Errors

Standard Error Response

@/app/api/example/route.ts
import { NextResponse } from 'next/server';

export async function POST(req: Request) {
  try {
    const body = await req.json();
    
    if (!body.email) {
      return NextResponse.json(
        { error: 'Email is required' },
        { status: 400 }
      );
    }

    const result = await processData(body);
    return NextResponse.json({ data: result });
  } catch (error) {
    console.error('API error:', error);
    return NextResponse.json(
      { error: 'Internal server error' },
      { status: 500 }
    );
  }
}

Validation with Zod

API Validation
import { z } from 'zod';

const schema = z.object({
  email: z.string().email(),
  name: z.string().min(1),
});

export async function POST(req: Request) {
  try {
    const body = await req.json();
    const validated = schema.parse(body);
    return NextResponse.json({ success: true });
  } catch (error) {
    if (error instanceof z.ZodError) {
      return NextResponse.json(
        { error: 'Validation failed', details: error.errors },
        { status: 400 }
      );
    }
    return NextResponse.json({ error: 'Internal error' }, { status: 500 });
  }
}

Server Component Errors

Try-Catch Pattern
export default async function Page() {
  try {
    const data = await fetchData();
    return <div>{data.title}</div>;
  } catch (error) {
    console.error('Failed to fetch data:', error);
    return <div>Failed to load content</div>;
  }
}

Error Boundaries

Route Error Boundary

@/app/(base)/error.tsx
'use client';

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  return (
    <div className="flex flex-col items-center gap-4 p-8">
      <h2 className="text-2xl font-bold">Something went wrong</h2>
      <p className="text-neutral-foreground">{error.message}</p>
      <button onClick={reset} className="btn">
        Try again
      </button>
    </div>
  );
}

Not Found Page

@/app/(base)/not-found.tsx
import { NotFoundIllustration } from '@/components/svgs/NotFoundIllustration';

export default function NotFound() {
  return (
    <div className="flex flex-col items-center gap-4">
      <NotFoundIllustration size={200} />
      <h2 className="text-2xl font-bold">Page Not Found</h2>
      <a href="/" className="btn">Go Home</a>
    </div>
  );
}

Toast Notifications

Toast Examples
import { toast } from 'sonner';

// Success
toast.success('Profile updated successfully');

// Error
toast.error('Failed to save changes');

// Custom
toast('Processing...', {
  description: 'This may take a few moments',
  duration: 5000,
});

Database Errors

Prisma Error Handling
import { Prisma } from '@prisma/client';

try {
  const user = await prisma.user.create({ data });
} catch (error) {
  if (error instanceof Prisma.PrismaClientKnownRequestError) {
    if (error.code === 'P2002') {
      return { error: 'Email already exists' };
    }
  }
  return { error: 'Database error' };
}

Loading States

Skeleton Component
export function BaseFormSkeleton() {
  return (
    <div className="flex flex-col gap-4 animate-pulse">
      <div className="h-10 bg-neutral rounded" />
      <div className="h-10 bg-neutral rounded" />
      <div className="h-10 bg-neutral rounded" />
    </div>
  );
}
Conditional Rendering
import { BaseFormSkeleton } from '@/components/user/BaseFormSkeleton';

export function SignInForm() {
  const { isLoaded } = useSignIn();

  if (!isLoaded) {
    return <BaseFormSkeleton />;
  }

  return <form>{/* Form content */}</form>;
}

Related

  • Next.js Error Handling - Official docs
  • Zod Validation - Schema validation
  • Sonner - Toast notifications

How is this guide ?

Last updated on

Data Fetching Patterns

Server-side data fetching patterns using lib functions that call API routes for clean separation of concerns.

On this page

Form Validation Errors
Zod Schema Errors
Manual Field Errors
API Error Handling
Clerk API Errors
Stripe API Errors
API Route Errors
Standard Error Response
Validation with Zod
Server Component Errors
Error Boundaries
Route Error Boundary
Not Found Page
Toast Notifications
Database Errors
Loading States
Related