Falytom logoFALYTOM
Games
ToolsUI ComponentsServicesProductsAbout
menu
  1. Home
  2. /Web Development
  3. /Nextjs
  4. /Error Handling
Your Progress0%
0 of 70 completed

NextJS Topics

Getting Started

  • What is Next.js?
  • Next.js vs React - Key Differences
  • Creating Your First Next.js 15 Project
  • Next.js Project Structure Explained
  • App Router vs Pages Router

Routing Fundamentals

  • File-Based Routing Basics
  • Creating Pages with page.tsx
  • Dynamic Routes and Route Parameters
  • Catch-All and Optional Catch-All Routes
  • Route Groups for Organization
  • Parallel Routes
  • Intercepting Routes

Layouts and Pages

  • Understanding Layouts
  • Root Layout and Global Configuration
  • Nested Layouts
  • Templates vs Layouts
  • Loading States with loading.tsx
  • Error Handling with error.tsx

Server and Client Components

  • Understanding Server Components
  • Client Components with 'use client'
  • When to Use Server vs Client Components
  • Component Composition Patterns
  • Passing Props Between Server and Client
  • Server Component Patterns and Best Practices

Data Fetching

  • Fetching Data in Server Components
  • Parallel and Sequential Data Fetching
  • Data Fetching Patterns and Strategies
  • Caching and Revalidation
  • Revalidate and Cache Tags
  • Handling Loading and Error States in Data Fetching

Navigation and Links

  • Link Component Basics
  • useRouter Hook for Programmatic Navigation
  • usePathname and useSearchParams Hooks
  • Active Links and Navigation States
  • Redirects and Navigation Guards

Styling in Next.js

  • CSS Modules in Next.js
  • Tailwind CSS Setup and Configuration
  • Global Styles and CSS Variables
  • Next.js Font Optimization

Images and Media

  • Next.js Image Component Basics
  • Image Optimization and Best Practices
  • Working with Static Assets

Forms and Data Mutations

  • Understanding Server Actions
  • Form Handling with Server Actions
  • Form Validation and Error Handling
  • useFormStatus and useFormState Hooks
  • Revalidating Data After Mutations
  • Optimistic Updates

Metadata and SEO

  • Static Metadata Configuration
  • Dynamic Metadata Generation
  • Open Graph and Social Media Cards
  • Sitemap and Robots.txt

API Routes and Route Handlers

  • Route Handlers Introduction
  • GET and POST Request Handlers
  • Dynamic API Routes
  • Request and Response Objects
  • API Error Handling and Status Codes

Middleware and Advanced Features

  • Introduction to Middleware
  • Authentication with Middleware
  • Environment Variables and Configuration
  • Streaming and Suspense
  • Not Found and Global Error Pages

Deployment and Optimization

  • Understanding Static and Dynamic Rendering
  • generateStaticParams for Static Generation
  • Build and Production Optimization
  • Deploying to Vercel
  • Alternative Deployment Options
  • Performance Monitoring and Analytics

Real-World Projects

  • Project 1: E-commerce Product Catalog
  • Project 2: Dashboard with Authentication

Error Handling with error.tsx

Creating resilient applications with graceful error boundaries

Things go wrong. APIs fail, data is malformed, networks drop. Without proper error handling, your entire app could crash with a cryptic white screen. Next.js makes error handling elegant with error.tsx files that automatically create React Error Boundaries. When an error occurs, users see a helpful error UI with recovery options—not a broken page. Let's build resilient applications that handle failures gracefully and keep users informed!

What Is error.tsx?

error.tsx is a special file that defines error UI for a route segment:

  • Automatic Error Boundary: Next.js wraps pages with React Error Boundary
  • Catches runtime errors: Any error in page or children
  • Recovery option: Reset function to retry the operation
  • Keeps layout intact: Error replaces page content, not layout

Error Handling Structure

Each route segment can have its own error.tsx

appImportant

Select a file or folder to see details

How It Works

When you create error.tsx, Next.js automatically wraps your page:

TYPESCRIPT
<Layout>
  <ErrorBoundary fallback={<YourErrorUI />}>
    <Page />
  </ErrorBoundary>
</Layout>

If an error occurs in the page, it's caught and your error UI shows instead!

Creating Your First Error Handler

Step 1: Basic Error Component

app/error.tsx
'use client'; // Error components must be Client Components

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  return (
    <div className="flex items-center justify-center min-h-screen bg-gray-50">
      <div className="text-center">
        <h2 className="text-2xl font-bold text-red-600 mb-4">
          Something went wrong!
        </h2>
        <p className="text-gray-600 mb-6">
          {error.message}
        </p>
        <button
          onClick={reset}
          className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
        >
          Try again
        </button>
      </div>
    </div>
  );
}

⚠️ Must Be Client Component

Error components must have 'use client' at the top because they use hooks and event handlers.

Step 2: Better Error UI

app/error.tsx
'use client';

import { useEffect } from 'react';

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  useEffect(() => {
    // Log error to error reporting service
    console.error('Error:', error);
  }, [error]);

  return (
    <div className="flex items-center justify-center min-h-screen bg-gray-50 px-4">
      <div className="max-w-md w-full bg-white rounded-lg shadow-lg p-8">
        {/* Error icon */}
        <div className="flex justify-center mb-6">
          <div className="w-16 h-16 bg-red-100 rounded-full flex items-center justify-center">
            <svg
              className="w-8 h-8 text-red-600"
              fill="none"
              stroke="currentColor"
              viewBox="0 0 24 24"
            >
              <path
                strokeLinecap="round"
                strokeLinejoin="round"
                strokeWidth={2}
                d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
              />
            </svg>
          </div>
        </div>

        {/* Error heading */}
        <h2 className="text-2xl font-bold text-center text-gray-900 mb-2">
          Oops! Something went wrong
        </h2>

        {/* Error message */}
        <p className="text-center text-gray-600 mb-6">
          {error.message || 'An unexpected error occurred'}
        </p>

        {/* Error ID (if available) */}
        {error.digest && (
          <p className="text-center text-sm text-gray-500 mb-6">
            Error ID: {error.digest}
          </p>
        )}

        {/* Action buttons */}
        <div className="flex flex-col sm:flex-row gap-3">
          <button
            onClick={reset}
            className="flex-1 px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition"
          >
            Try Again
          </button>
          
            href="/"
            className="flex-1 px-6 py-3 bg-gray-200 text-gray-800 rounded-lg hover:bg-gray-300 transition text-center"
          >
            Go Home
          </a>
        </div>

        {/* Help text */}
        <p className="text-center text-sm text-gray-500 mt-6">
          If this problem persists, please{' '}
          <a href="/contact" className="text-blue-600 hover:underline">
            contact support
          </a>
        </p>
      </div>
    </div>
  );
}

Understanding the Props

error: Error & { digest?: string }

error.message: Human-readable error message

error.digest: Unique error ID (production only)

error.stack: Stack trace (development only)

reset: () => void

Function to attempt recovery. When called, Next.js will try to re-render the error boundary's contents.

Handling Different Error Scenarios

1. Network Errors

app/blog/error.tsx
'use client';

export default function BlogError({
  error,
  reset,
}: {
  error: Error;
  reset: () => void;
}) {
  const isNetworkError = error.message.includes('fetch') || 
                         error.message.includes('network');

  return (
    <div className="container mx-auto px-4 py-12">
      <div className="max-w-lg mx-auto text-center">
        <h2 className="text-2xl font-bold mb-4">
          {isNetworkError 
            ? 'Connection Problem' 
            : 'Something Went Wrong'}
        </h2>
        
        <p className="text-gray-600 mb-6">
          {isNetworkError
            ? 'We couldn\'t load the blog posts. Please check your internet connection.'
            : error.message}
        </p>

        <button
          onClick={reset}
          className="px-6 py-3 bg-blue-600 text-white rounded"
        >
          {isNetworkError ? 'Retry Connection' : 'Try Again'}
        </button>
      </div>
    </div>
  );
}

2. Not Found vs Error

app/blog/[slug]/error.tsx
'use client';

export default function PostError({
  error,
  reset,
}: {
  error: Error;
  reset: () => void;
}) {
  const isNotFound = error.message.includes('NEXT_NOT_FOUND');

  if (isNotFound) {
    return (
      <div className="container mx-auto px-4 py-12">
        <h2 className="text-2xl font-bold mb-4">Post Not Found</h2>
        <p className="mb-6">This blog post doesn't exist.</p>
        <a href="/blog" className="text-blue-600 hover:underline">
          ← Back to Blog
        </a>
      </div>
    );
  }

  return (
    <div className="container mx-auto px-4 py-12">
      <h2 className="text-2xl font-bold mb-4">Error Loading Post</h2>
      <p className="mb-6">{error.message}</p>
      <button onClick={reset} className="px-6 py-3 bg-blue-600 text-white rounded">
        Try Again
      </button>
    </div>
  );
}

3. Authentication Errors

app/dashboard/error.tsx
'use client';

import { useRouter } from 'next/navigation';

export default function DashboardError({
  error,
  reset,
}: {
  error: Error;
  reset: () => void;
}) {
  const router = useRouter();
  const isAuthError = error.message.includes('Unauthorized') ||
                      error.message.includes('Authentication');

  if (isAuthError) {
    return (
      <div className="flex items-center justify-center min-h-screen">
        <div className="text-center">
          <h2 className="text-2xl font-bold mb-4">
            Authentication Required
          </h2>
          <p className="text-gray-600 mb-6">
            Please log in to access the dashboard.
          </p>
          <button
            onClick={() => router.push('/login')}
            className="px-6 py-3 bg-blue-600 text-white rounded"
          >
            Go to Login
          </button>
        </div>
      </div>
    );
  }

  return (
    <div className="flex items-center justify-center min-h-screen">
      <div className="text-center">
        <h2 className="text-2xl font-bold mb-4">Dashboard Error</h2>
        <p className="text-gray-600 mb-6">{error.message}</p>
        <button onClick={reset} className="px-6 py-3 bg-blue-600 text-white rounded">
          Try Again
        </button>
      </div>
    </div>
  );
}

Nested Error Boundaries

Like loading states, errors cascade from specific to general:

PLAINTEXT
app/
  error.tsx                 ← Catches all app errors
  dashboard/
    error.tsx               ← Catches dashboard errors
    analytics/
      error.tsx             ← Catches only analytics errors
      page.tsx

Error in: /dashboard/analytics

PLAINTEXT
<RootLayout>
  <DashboardLayout>  ← Layout stays intact
    <ErrorBoundary fallback={<AnalyticsError />}>
      <AnalyticsPage />  ← Error caught here
    </ErrorBoundary>
  </DashboardLayout>
</RootLayout>

Shows: Analytics error UI
Dashboard layout remains visible!

Layout Errors

error.tsx does NOT catch errors in layouts at the same level. To catch layout errors, you need an error.tsx in the parent folder.

Global Error Handler

For errors in the root layout, use global-error.tsx:

app/global-error.tsx
'use client';

export default function GlobalError({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  return (
    <html>
      <body>
        <div className="flex items-center justify-center min-h-screen bg-gray-50 px-4">
          <div className="max-w-md w-full text-center">
            <h2 className="text-3xl font-bold text-red-600 mb-4">
              Application Error
            </h2>
            <p className="text-gray-600 mb-6">
              A critical error occurred. Please try refreshing the page.
            </p>
            <button
              onClick={reset}
              className="px-6 py-3 bg-blue-600 text-white rounded-lg"
            >
              Refresh Application
            </button>
          </div>
        </div>
      </body>
    </html>
  );
}

Important: global-error.tsx Must Include HTML

Since it replaces the root layout when active, global-error.tsx must include <html> and <body> tags.

Error Logging and Monitoring

Logging to External Service

app/error.tsx
'use client';

import { useEffect } from 'react';
import * as Sentry from '@sentry/nextjs';

export default function Error({
  error,
  reset,
}: {
  error: Error;
  reset: () => void;
}) {
  useEffect(() => {
    // Log to Sentry
    Sentry.captureException(error);

    // Or log to your custom service
    fetch('/api/log-error', {
      method: 'POST',
      body: JSON.stringify({
        message: error.message,
        stack: error.stack,
        timestamp: new Date().toISOString(),
        userAgent: navigator.userAgent,
      }),
    });
  }, [error]);

  return (
    <div>
      <h2>Error occurred</h2>
      <button onClick={reset}>Try again</button>
    </div>
  );
}

API Route for Error Logging

app/api/log-error/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  try {
    const error = await request.json();
    
    // Log to your database or logging service
    console.error('Client Error:', {
      message: error.message,
      stack: error.stack,
      timestamp: error.timestamp,
      userAgent: error.userAgent,
    });

    // Could also send to external service
    // await sendToLoggingService(error);

    return NextResponse.json({ success: true });
  } catch (err) {
    return NextResponse.json(
      { error: 'Failed to log error' },
      { status: 500 }
    );
  }
}

Error Recovery Patterns

1. Simple Reset

TYPESCRIPT
<button onClick={reset}>
  Try Again
</button>

2. Reset with Loading State

TYPESCRIPT
'use client';

import { useState } from 'react';

export default function Error({ error, reset }) {
  const [isResetting, setIsResetting] = useState(false);

  const handleReset = async () => {
    setIsResetting(true);
    await new Promise(resolve => setTimeout(resolve, 500));
    reset();
  };

  return (
    <div>
      <h2>Error: {error.message}</h2>
      <button
        onClick={handleReset}
        disabled={isResetting}
        className="px-6 py-3 bg-blue-600 text-white rounded disabled:opacity-50"
      >
        {isResetting ? 'Retrying...' : 'Try Again'}
      </button>
    </div>
  );
}

3. Alternative Actions

TYPESCRIPT
'use client';

import { useRouter } from 'next/navigation';

export default function Error({ error, reset }) {
  const router = useRouter();

  return (
    <div>
      <h2>Error Loading Data</h2>
      <p>{error.message}</p>
      
      <div className="flex gap-4">
        <button onClick={reset}>
          Try Again
        </button>
        <button onClick={() => router.back()}>
          Go Back
        </button>
        <button onClick={() => router.push('/')}>
          Go Home
        </button>
      </div>
    </div>
  );
}

4. Retry with Exponential Backoff

TYPESCRIPT
'use client';

import { useState } from 'react';

export default function Error({ error, reset }) {
  const [retryCount, setRetryCount] = useState(0);
  const [isRetrying, setIsRetrying] = useState(false);

  const handleRetry = async () => {
    setIsRetrying(true);
    setRetryCount(prev => prev + 1);
    
    // Exponential backoff: 1s, 2s, 4s, 8s
    const delay = Math.min(1000 * Math.pow(2, retryCount), 8000);
    await new Promise(resolve => setTimeout(resolve, delay));
    
    reset();
    setIsRetrying(false);
  };

  return (
    <div>
      <h2>Error: {error.message}</h2>
      <p>Retry attempt: {retryCount}</p>
      <button
        onClick={handleRetry}
        disabled={isRetrying}
      >
        {isRetrying ? 'Retrying...' : 'Try Again'}
      </button>
    </div>
  );
}

Error Handling Best Practices

1. Provide Clear Information

TYPESCRIPT
// ✅ Good: Clear, actionable
<div>
  <h2>Failed to Load Posts</h2>
  <p>We couldn't connect to the server. Please check your connection and try again.</p>
  <button onClick={reset}>Retry</button>
</div>

// ❌ Bad: Vague, unhelpful
<div>
  <h2>Error</h2>
  <p>Something went wrong</p>
</div>

2. Show Different Messages for Production

TYPESCRIPT
'use client';

export default function Error({ error, reset }) {
  const isDev = process.env.NODE_ENV === 'development';

  return (
    <div>
      <h2>Something went wrong</h2>
      
      {/* Show detailed errors in development */}
      {isDev && (
        <details className="mt-4 p-4 bg-gray-100 rounded">
          <summary>Error details</summary>
          <pre className="mt-2 text-xs overflow-auto">
            {error.message}
            {error.stack}
          </pre>
        </details>
      )}
      
      {/* Generic message in production */}
      {!isDev && (
        <p>An unexpected error occurred. Our team has been notified.</p>
      )}
      
      <button onClick={reset}>Try Again</button>
    </div>
  );
}

3. Maintain Brand Consistency

TYPESCRIPT
// Match your app's design system
export default function Error({ error, reset }) {
  return (
    <div className="container mx-auto px-4 py-12">
      <div className="max-w-lg mx-auto">
        {/* Use your brand colors */}
        <div className="bg-red-50 border-l-4 border-red-500 p-6 rounded">
          <h2 className="text-2xl font-bold text-red-900 mb-2">
            Oops!
          </h2>
          <p className="text-red-700">
            {error.message}
          </p>
        </div>
        
        {/* Use your button styles */}
        <button
          onClick={reset}
          className="mt-6 btn btn-primary"  // Your classes
        >
          Try Again
        </button>
      </div>
    </div>
  );
}

4. Test Error States

TYPESCRIPT
// Add a test route that throws errors
// app/test-error/page.tsx
export default function TestError() {
  throw new Error('Test error for development');
  return null;
}

// Visit /test-error to see your error UI

5. Provide Context-Appropriate Actions

  • Network errors: Retry button
  • Auth errors: Login button
  • Not found errors: Back or home link
  • Permission errors: Contact support link

Complete Practical Example

Production-Ready Error Component

A complete error UI with logging and recovery options

error.tsx

Output Preview

Click "Run Code" to see the output

Special Case: not-found.tsx

For 404 errors, use not-found.tsx instead of error.tsx:

app/not-found.tsx
import Link from 'next/link';

export default function NotFound() {
  return (
    <div className="flex items-center justify-center min-h-screen bg-gray-50">
      <div className="text-center">
        <h1 className="text-6xl font-bold text-gray-900 mb-4">404</h1>
        <h2 className="text-2xl font-semibold text-gray-700 mb-4">
          Page Not Found
        </h2>
        <p className="text-gray-600 mb-8">
          The page you're looking for doesn't exist.
        </p>
        <Link
          href="/"
          className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
        >
          Go Home
        </Link>
      </div>
    </div>
  );
}

Trigger not-found programmatically:

TYPESCRIPT
import { notFound } from 'next/navigation';

export default async function Page({ params }) {
  const post = await getPost(params.slug);
  
  if (!post) {
    notFound();  // Shows not-found.tsx
  }
  
  return <div>{post.content}</div>;
}

Key Takeaways

  • error.tsx creates automatic Error Boundaries - no manual setup
  • Must be Client Components - use 'use client' directive
  • Receives error and reset props - show error, provide recovery
  • Each route segment can have its own error handler - granular errors
  • Layouts stay intact - only page content shows error
  • Use global-error.tsx for root layout errors - must include HTML
  • Log errors for monitoring - track issues in production
  • Provide clear, actionable error messages - help users recover

You've Completed Layouts & Pages! 🎉

Congratulations! You've finished the entire Layouts & Pages section, mastering:

What You've Learned

📐 Layouts

  • Understanding layouts
  • Root layout configuration
  • Nested layouts
  • Templates vs layouts

⚡ Loading & Errors

  • Loading states with loading.tsx
  • Error handling with error.tsx
  • Skeleton screens
  • Error boundaries

You now have all the tools to build sophisticated Next.js applications with proper structure, loading states, and error handling. Your apps will be resilient, user-friendly, and production-ready!

🚀 What's Next?

You've mastered the fundamentals of layouts and pages. The next topics in your Next.js journey will cover data fetching, server and client components, caching, and more advanced patterns. You're building a solid foundation!

Test Your Understanding

Question 1 of 4

What does error.tsx automatically create?

Master error handling in Next.js! Learn how to create resilient apps with graceful error boundaries.

Previous
Loading States with loading.tsx
Next
Understanding Server Components

Continue Your Next.js Mastery

Join 2,000+ developers building production Next.js applications. Get more advanced tutorials delivered to your inbox - 100% FREE.

You might also like

UI Components

Ready-made components for your projects

Developer Tools

Boost productivity with handy generators

HTML Game

Learn HTML concepts through interactive gameplay

Join Our Web Development Training Program

Master frontend & backend development with hands-on projects.

NextJS Tutorials

0 of 70 completed

Your Progress0%

Topics

Getting Started

  • What is Next.js?
  • Next.js vs React - Key Differences
  • Creating Your First Next.js 15 Project
  • Next.js Project Structure Explained
  • App Router vs Pages Router

Routing Fundamentals

  • File-Based Routing Basics
  • Creating Pages with page.tsx
  • Dynamic Routes and Route Parameters
  • Catch-All and Optional Catch-All Routes
  • Route Groups for Organization
  • Parallel Routes
  • Intercepting Routes

Layouts and Pages

  • Understanding Layouts
  • Root Layout and Global Configuration
  • Nested Layouts
  • Templates vs Layouts
  • Loading States with loading.tsx
  • Error Handling with error.tsx

Server and Client Components

  • Understanding Server Components
  • Client Components with 'use client'
  • When to Use Server vs Client Components
  • Component Composition Patterns
  • Passing Props Between Server and Client
  • Server Component Patterns and Best Practices

Data Fetching

  • Fetching Data in Server Components
  • Parallel and Sequential Data Fetching
  • Data Fetching Patterns and Strategies
  • Caching and Revalidation
  • Revalidate and Cache Tags
  • Handling Loading and Error States in Data Fetching

Navigation and Links

  • Link Component Basics
  • useRouter Hook for Programmatic Navigation
  • usePathname and useSearchParams Hooks
  • Active Links and Navigation States
  • Redirects and Navigation Guards

Styling in Next.js

  • CSS Modules in Next.js
  • Tailwind CSS Setup and Configuration
  • Global Styles and CSS Variables
  • Next.js Font Optimization

Images and Media

  • Next.js Image Component Basics
  • Image Optimization and Best Practices
  • Working with Static Assets

Forms and Data Mutations

  • Understanding Server Actions
  • Form Handling with Server Actions
  • Form Validation and Error Handling
  • useFormStatus and useFormState Hooks
  • Revalidating Data After Mutations
  • Optimistic Updates

Metadata and SEO

  • Static Metadata Configuration
  • Dynamic Metadata Generation
  • Open Graph and Social Media Cards
  • Sitemap and Robots.txt

API Routes and Route Handlers

  • Route Handlers Introduction
  • GET and POST Request Handlers
  • Dynamic API Routes
  • Request and Response Objects
  • API Error Handling and Status Codes

Middleware and Advanced Features

  • Introduction to Middleware
  • Authentication with Middleware
  • Environment Variables and Configuration
  • Streaming and Suspense
  • Not Found and Global Error Pages

Deployment and Optimization

  • Understanding Static and Dynamic Rendering
  • generateStaticParams for Static Generation
  • Build and Production Optimization
  • Deploying to Vercel
  • Alternative Deployment Options
  • Performance Monitoring and Analytics

Real-World Projects

  • Project 1: E-commerce Product Catalog
  • Project 2: Dashboard with Authentication
Falytom logoFALYTOM

Learn technologies like HTML, CSS, JavaScript, React, NodeJS and more through interactive games, tutorials, components and free tools. Transform your businesses with AI chatbots, SEO-optimized websites, and professional development services.

© 2025 Tomilola Group
  • Facebook logo
  • Twitter logo
  • Instagram logo
  • LinkedIn logo
  • GitHub logo
  • YouTube logo