Falytom logoFALYTOM
Games
ToolsUI ComponentsServicesProductsAbout
menu
  1. Home
  2. /Web Development
  3. /Nextjs
  4. /Not Found Error Pages
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

Not Found and Global Error Pages

Custom 404 and error handling

Errors happen—resources don't exist, data fails to load, or unexpected issues occur. Custom error pages provide better user experiences than generic error screens. Create not-found.tsx for 404 pages, error.tsx for error boundaries, and global-error.tsx for root-level errors. Build helpful, branded error pages that guide users back to working parts of your app!

Not Found Pages (404)

Root not-found.tsx

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

export default function NotFound() {
  return (
    <div className="flex min-h-screen flex-col items-center justify-center">
      <h1 className="text-6xl font-bold text-gray-900 mb-4">404</h1>
      <h2 className="text-2xl font-semibold text-gray-700 mb-2">
        Page Not Found
      </h2>
      <p className="text-gray-600 mb-8 text-center max-w-md">
        Sorry, we couldn't find the page you're looking for.
      </p>
      
      <div className="flex gap-4">
        <Link
          href="/"
          className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
        >
          Go Home
        </Link>
        <Link
          href="/contact"
          className="px-6 py-3 border border-gray-300 rounded-lg hover:bg-gray-50"
        >
          Contact Support
        </Link>
      </div>
    </div>
  );
}

// ✅ Shown when route doesn't exist
// ✅ Custom 404 UI
// ✅ Navigation links
// ✅ Branded experience

Triggering notFound() Programmatically

app/blog/[slug]/page.tsx
import { notFound } from 'next/navigation';

async function getPost(slug: string) {
  const post = await db.posts.findUnique({
    where: { slug },
  });
  
  return post;
}

export default async function BlogPostPage({
  params,
}: {
  params: { slug: string };
}) {
  const post = await getPost(params.slug);
  
  // Trigger 404 if post doesn't exist
  if (!post) {
    notFound();
  }
  
  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </article>
  );
}

// ✅ Check if resource exists
// ✅ Call notFound() if not found
// ✅ Shows not-found.tsx
// ✅ Returns 404 status code

Route-Specific not-found.tsx

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

export default function DashboardNotFound() {
  return (
    <div className="p-8">
      <h1 className="text-4xl font-bold mb-4">Dashboard Page Not Found</h1>
      <p className="text-gray-600 mb-6">
        The dashboard page you're looking for doesn't exist.
      </p>
      
      <div className="flex gap-4">
        <Link
          href="/dashboard"
          className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
        >
          Dashboard Home
        </Link>
        <Link
          href="/dashboard/settings"
          className="px-4 py-2 border rounded hover:bg-gray-50"
        >
          Settings
        </Link>
      </div>
    </div>
  );
}

// ✅ Specific to dashboard routes
// ✅ Contextual navigation
// ✅ Maintains dashboard layout
// ✅ More helpful than generic 404

Nested not-found.tsx

TYPESCRIPT
// File structure:
app/
  not-found.tsx           // Root 404
  dashboard/
    not-found.tsx         // Dashboard 404
    [id]/
      not-found.tsx       // Specific dashboard item 404

// Priority (most specific first):
// 1. /dashboard/[id]/not-found.tsx    - Most specific
// 2. /dashboard/not-found.tsx         - Section-specific
// 3. /app/not-found.tsx               - Root fallback

// ✅ More specific not-found.tsx overrides parent
// ✅ Contextual error messages
// ✅ Appropriate navigation options

Error Boundaries (error.tsx)

Basic error.tsx

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

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 min-h-screen flex-col items-center justify-center p-8">
      <h2 className="text-3xl font-bold text-gray-900 mb-4">
        Something went wrong!
      </h2>
      <p className="text-gray-600 mb-8 text-center max-w-md">
        We're sorry, but something unexpected happened. Please try again.
      </p>
      
      <button
        onClick={reset}
        className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
      >
        Try Again
      </button>
    </div>
  );
}

// ✅ Must be Client Component ('use client')
// ✅ Receives error and reset props
// ✅ reset() attempts to re-render
// ✅ Catches errors in route segment

Error with More Details

app/error.tsx
'use client';

import { useEffect } from 'react';
import Link from 'next/link';

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

  return (
    <div className="flex min-h-screen flex-col items-center justify-center p-8">
      <div className="max-w-lg w-full">
        <h1 className="text-4xl font-bold text-red-600 mb-4">
          Oops! Something Went Wrong
        </h1>
        
        <div className="bg-red-50 border border-red-200 rounded-lg p-4 mb-6">
          <p className="font-semibold text-red-800 mb-2">Error Details:</p>
          <p className="text-red-700 text-sm">{error.message}</p>
          {error.digest && (
            <p className="text-red-600 text-xs mt-2">
              Error ID: {error.digest}
            </p>
          )}
        </div>
        
        <div className="flex gap-4">
          <button
            onClick={reset}
            className="flex-1 px-4 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
          >
            Try Again
          </button>
          <Link
            href="/"
            className="flex-1 px-4 py-3 border border-gray-300 text-center rounded-lg hover:bg-gray-50"
          >
            Go Home
          </Link>
        </div>
        
        <Link
          href="/contact"
          className="block text-center text-blue-600 hover:underline mt-6"
        >
          Contact Support
        </Link>
      </div>
    </div>
  );
}

// ✅ Shows error message
// ✅ Error ID for support
// ✅ Multiple recovery options
// ✅ Support link

Route-Specific error.tsx

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

import { useEffect } from 'react';

export default function DashboardError({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  useEffect(() => {
    console.error('Dashboard error:', error);
  }, [error]);

  return (
    <div className="p-8">
      <div className="max-w-2xl mx-auto">
        <h1 className="text-3xl font-bold mb-4">Dashboard Error</h1>
        <p className="text-gray-600 mb-6">
          We encountered an error loading your dashboard. This might be a
          temporary issue.
        </p>
        
        <div className="bg-yellow-50 border border-yellow-200 rounded p-4 mb-6">
          <p className="text-yellow-800 text-sm">
            {error.message}
          </p>
        </div>
        
        <div className="flex gap-4">
          <button
            onClick={reset}
            className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
          >
            Reload Dashboard
          </button>
          <button
            onClick={() => window.location.reload()}
            className="px-4 py-2 border rounded hover:bg-gray-50"
          >
            Refresh Page
          </button>
        </div>
      </div>
    </div>
  );
}

// ✅ Dashboard-specific error UI
// ✅ Contextual messaging
// ✅ Maintains dashboard layout
// ✅ Multiple recovery options

Error Hierarchy

TYPESCRIPT
// File structure:
app/
  error.tsx              // Root error handler
  layout.tsx             // Root layout (NOT caught by error.tsx)
  dashboard/
    error.tsx            // Dashboard errors
    layout.tsx           // Dashboard layout (caught by parent error.tsx)
    [id]/
      error.tsx          // Specific dashboard item errors
      page.tsx           // Page (caught by closest error.tsx)

// Error boundary hierarchy:
// 1. Errors in page.tsx caught by closest error.tsx
// 2. Errors in layout.tsx caught by parent error.tsx
// 3. Errors in root layout.tsx caught by global-error.tsx

// ✅ Closest error.tsx catches error
// ✅ Layouts caught by parent error.tsx
// ✅ Root layout needs global-error.tsx

Global Error Handler

global-error.tsx

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

import { useEffect } from 'react';

export default function GlobalError({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  useEffect(() => {
    console.error('Global error:', error);
  }, [error]);

  return (
    <html>
      <body>
        <div className="flex min-h-screen flex-col items-center justify-center p-8">
          <h1 className="text-4xl font-bold text-red-600 mb-4">
            Critical Error
          </h1>
          <p className="text-gray-600 mb-8 max-w-md text-center">
            A critical error occurred. Please try reloading the page.
          </p>
          
          <button
            onClick={reset}
            className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
          >
            Reload Page
          </button>
        </div>
      </body>
    </html>
  );
}

// ✅ Catches errors in root layout
// ✅ Must include <html> and <body>
// ✅ Only used in production
// ✅ Fallback for catastrophic errors

⚠️ global-error.tsx Requirements

  • Must be a Client Component (use 'use client')
  • Must include full HTML structure (html, body tags)
  • Only catches errors in root layout.tsx
  • Only active in production (development shows overlay)
  • Replaces entire page including root layout

When to Use global-error.tsx

TYPESCRIPT
// global-error.tsx catches errors that error.tsx cannot:

// ❌ error.tsx CANNOT catch:
app/
  layout.tsx          // Root layout errors
  error.tsx           // Cannot catch its parent layout

// ✅ global-error.tsx CATCHES:
app/
  layout.tsx          // Root layout errors
  global-error.tsx    // Catches root layout errors

// Use cases:
// - Database connection errors in root layout
// - Authentication provider errors
// - Critical configuration errors
// - Any error in root layout.tsx

// Most apps need:
// 1. error.tsx for route errors
// 2. global-error.tsx for root layout errors

Error Pages File Structure

Organization of not-found and error pages

appImportant

Select a file or folder to see details

Practical Error Page Examples

Example 1: Product Not Found

app/products/[id]/page.tsx
import { notFound } from 'next/navigation';

async function getProduct(id: string) {
  const product = await db.products.findUnique({
    where: { id },
  });
  
  return product;
}

export default async function ProductPage({
  params,
}: {
  params: { id: string };
}) {
  const product = await getProduct(params.id);
  
  if (!product) {
    notFound();
  }
  
  return (
    <div>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
      <p className="text-2xl font-bold">${product.price}</p>
    </div>
  );
}

// ✅ Check product exists
// ✅ Call notFound() if missing
// ✅ Shows not-found.tsx
app/products/[id]/not-found.tsx
import Link from 'next/link';

export default function ProductNotFound() {
  return (
    <div className="max-w-2xl mx-auto p-8 text-center">
      <div className="mb-8">
        <svg
          className="w-24 h-24 mx-auto text-gray-400"
          fill="none"
          viewBox="0 0 24 24"
          stroke="currentColor"
        >
          <path
            strokeLinecap="round"
            strokeLinejoin="round"
            strokeWidth={2}
            d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"
          />
        </svg>
      </div>
      
      <h1 className="text-3xl font-bold mb-4">Product Not Found</h1>
      <p className="text-gray-600 mb-8">
        Sorry, the product you're looking for doesn't exist or has been removed.
      </p>
      
      <div className="flex gap-4 justify-center">
        <Link
          href="/products"
          className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
        >
          Browse Products
        </Link>
        <Link
          href="/search"
          className="px-6 py-3 border border-gray-300 rounded-lg hover:bg-gray-50"
        >
          Search
        </Link>
      </div>
    </div>
  );
}

// ✅ Product-specific 404
// ✅ Icon for visual feedback
// ✅ Helpful navigation
// ✅ Contextual messaging

Example 2: API Error with Retry

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

import { useEffect, useState } from 'react';

export default function DashboardError({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  const [retrying, setRetrying] = useState(false);

  useEffect(() => {
    console.error('Dashboard error:', error);
  }, [error]);

  const handleRetry = async () => {
    setRetrying(true);
    
    // Wait a bit before retrying
    await new Promise(resolve => setTimeout(resolve, 1000));
    
    reset();
    setRetrying(false);
  };

  return (
    <div className="max-w-2xl mx-auto p-8">
      <div className="bg-red-50 border-2 border-red-200 rounded-lg p-6 mb-6">
        <h2 className="text-2xl font-bold text-red-800 mb-2">
          Failed to Load Dashboard
        </h2>
        <p className="text-red-700 mb-4">
          {error.message || 'An unexpected error occurred'}
        </p>
        {error.digest && (
          <p className="text-red-600 text-sm">
            Error Reference: {error.digest}
          </p>
        )}
      </div>
      
      <div className="space-y-4">
        <button
          onClick={handleRetry}
          disabled={retrying}
          className="w-full px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed"
        >
          {retrying ? 'Retrying...' : 'Try Again'}
        </button>
        
        <button
          onClick={() => window.location.href = '/'}
          className="w-full px-6 py-3 border border-gray-300 rounded-lg hover:bg-gray-50"
        >
          Return to Home
        </button>
      </div>
      
      <div className="mt-8 text-center text-sm text-gray-600">
        <p>Still having issues?</p>
        
          href="/contact"
          className="text-blue-600 hover:underline"
        >
          Contact our support team
        </a>
      </div>
    </div>
  );
}

// ✅ Retry with loading state
// ✅ Error details visible
// ✅ Multiple escape routes
// ✅ Support link

Example 3: User-Friendly Error Messages

app/error.tsx
'use client';

import { useEffect } from 'react';

function getErrorMessage(error: Error): {
  title: string;
  message: string;
  action: string;
} {
  // Parse error for user-friendly messages
  if (error.message.includes('fetch failed')) {
    return {
      title: 'Connection Error',
      message: 'We couldn't connect to our servers. Please check your internet connection.',
      action: 'Try again when you're back online.',
    };
  }
  
  if (error.message.includes('timeout')) {
    return {
      title: 'Request Timeout',
      message: 'The request took too long to complete.',
      action: 'Please try again.',
    };
  }
  
  if (error.message.includes('unauthorized')) {
    return {
      title: 'Authentication Required',
      message: 'Your session may have expired.',
      action: 'Please log in again.',
    };
  }
  
  // Default message
  return {
    title: 'Something Went Wrong',
    message: 'We encountered an unexpected error.',
    action: 'Please try again or contact support if the problem persists.',
  };
}

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

  const { title, message, action } = getErrorMessage(error);

  return (
    <div className="flex min-h-screen items-center justify-center p-4">
      <div className="max-w-md w-full">
        <div className="text-center mb-8">
          <div className="inline-flex items-center justify-center w-16 h-16 bg-red-100 rounded-full mb-4">
            <svg
              className="w-8 h-8 text-red-600"
              fill="none"
              viewBox="0 0 24 24"
              stroke="currentColor"
            >
              <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>
          
          <h1 className="text-2xl font-bold text-gray-900 mb-2">
            {title}
          </h1>
          <p className="text-gray-600 mb-1">
            {message}
          </p>
          <p className="text-sm text-gray-500">
            {action}
          </p>
        </div>
        
        <button
          onClick={reset}
          className="w-full px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
        >
          Try Again
        </button>
      </div>
    </div>
  );
}

// ✅ User-friendly error messages
// ✅ Specific guidance based on error
// ✅ Clean, professional UI
// ✅ Icon for visual feedback

Error Recovery Patterns

Automatic Retry Pattern

app/error.tsx
'use client';

import { useEffect, useState } from 'react';

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  const [retryCount, setRetryCount] = useState(0);
  const maxRetries = 3;

  useEffect(() => {
    console.error('Error:', error);

    // Auto-retry for transient errors
    if (retryCount < maxRetries && isTransientError(error)) {
      const timeout = setTimeout(() => {
        console.log(`Auto-retry attempt ${retryCount + 1}`);
        setRetryCount(prev => prev + 1);
        reset();
      }, 2000 * (retryCount + 1)); // Exponential backoff

      return () => clearTimeout(timeout);
    }
  }, [error, reset, retryCount]);

  return (
    <div className="p-8 text-center">
      {retryCount < maxRetries ? (
        <div>
          <h2 className="text-xl font-semibold mb-2">Attempting to recover...</h2>
          <p className="text-gray-600">
            Retry attempt {retryCount + 1} of {maxRetries}
          </p>
        </div>
      ) : (
        <div>
          <h2 className="text-xl font-semibold mb-2">Unable to Recover</h2>
          <p className="text-gray-600 mb-4">{error.message}</p>
          <button
            onClick={() => {
              setRetryCount(0);
              reset();
            }}
            className="px-6 py-3 bg-blue-600 text-white rounded hover:bg-blue-700"
          >
            Try Again
          </button>
        </div>
      )}
    </div>
  );
}

function isTransientError(error: Error): boolean {
  const transientErrors = ['fetch failed', 'timeout', 'ECONNREFUSED'];
  return transientErrors.some(msg => error.message.includes(msg));
}

// ✅ Automatic retry for transient errors
// ✅ Exponential backoff
// ✅ Max retry limit
// ✅ Manual retry option

Partial Error Recovery

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

import { useState } from 'react';
import Link from 'next/link';

export default function DashboardError({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  const [showDetails, setShowDetails] = useState(false);

  return (
    <div className="max-w-4xl mx-auto p-8">
      <div className="mb-8">
        <h1 className="text-3xl font-bold mb-2">Dashboard Unavailable</h1>
        <p className="text-gray-600">
          We're having trouble loading your dashboard. You can still access other parts of the app.
        </p>
      </div>

      {/* Alternative actions */}
      <div className="grid grid-cols-2 gap-4 mb-8">
        <Link
          href="/profile"
          className="p-4 border rounded-lg hover:bg-gray-50 text-center"
        >
          <h3 className="font-semibold mb-1">View Profile</h3>
          <p className="text-sm text-gray-600">Check your profile settings</p>
        </Link>
        
        <Link
          href="/history"
          className="p-4 border rounded-lg hover:bg-gray-50 text-center"
        >
          <h3 className="font-semibold mb-1">View History</h3>
          <p className="text-sm text-gray-600">See your recent activity</p>
        </Link>
      </div>

      <button
        onClick={reset}
        className="w-full px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 mb-4"
      >
        Try Loading Dashboard Again
      </button>

      <button
        onClick={() => setShowDetails(!showDetails)}
        className="text-sm text-gray-600 hover:underline"
      >
        {showDetails ? 'Hide' : 'Show'} Error Details
      </button>

      {showDetails && (
        <div className="mt-4 p-4 bg-gray-100 rounded text-sm">
          <p className="font-mono">{error.message}</p>
          {error.digest && (
            <p className="mt-2 text-gray-600">ID: {error.digest}</p>
          )}
        </div>
      )}
    </div>
  );
}

// ✅ Alternative actions available
// ✅ Doesn't block entire app
// ✅ Optional error details
// ✅ Multiple recovery paths

Error Page Best Practices

1. Provide Clear Next Steps

TYPESCRIPT
// ✅ GOOD: Clear actions
<div>
  <h2>Page Not Found</h2>
  <p>The page you're looking for doesn't exist.</p>
  <Link href="/">Go to Homepage</Link>
  <Link href="/search">Search</Link>
  <Link href="/contact">Contact Support</Link>
</div>

// ❌ BAD: No guidance
<div>
  <h2>404</h2>
  <p>Not found</p>
</div>

// Always provide actionable next steps

2. Match Your Brand

TYPESCRIPT
// ✅ GOOD: Branded error page
export default function NotFound() {
  return (
    <div className="min-h-screen bg-gradient-to-b from-blue-50 to-white">
      <nav>{/* Your app's navbar */}</nav>
      <div className="container mx-auto">
        {/* Branded 404 content */}
      </div>
      <footer>{/* Your app's footer */}</footer>
    </div>
  );
}

// ❌ BAD: Generic error page
<div>404 Not Found</div>

// Make error pages feel part of your app

3. Log Errors Properly

TYPESCRIPT
// ✅ GOOD: Comprehensive logging
useEffect(() => {
  console.error('Error details:', {
    message: error.message,
    digest: error.digest,
    stack: error.stack,
    timestamp: new Date().toISOString(),
    url: window.location.href,
    userAgent: navigator.userAgent,
  });
  
  // Send to error tracking service
  if (process.env.NODE_ENV === 'production') {
    logToErrorService(error);
  }
}, [error]);

// Error tracking helps fix issues

4. Avoid Exposing Sensitive Info

TYPESCRIPT
// ✅ GOOD: Generic user-facing message
<p>We encountered an error loading your data.</p>
{error.digest && <p>Reference: {error.digest}</p>}

// ❌ BAD: Exposing internals
<p>Database connection failed: {databaseUrl}</p>
<p>API key invalid: {apiKey}</p>

// Never expose:
// - Database URLs
// - API keys
// - Internal paths
// - Stack traces (in production)

5. Test Error Pages

TYPESCRIPT
// Test not-found page:
// Visit /this-page-does-not-exist

// Test error.tsx:
// Create component that throws error
function BrokenComponent() {
  throw new Error('Test error');
  return <div>Never rendered</div>;
}

// Test in development:
export default function TestPage() {
  return (
    <div>
      <h1>Test Error Handling</h1>
      <BrokenComponent />
    </div>
  );
}

// ✅ Test all error scenarios
// ✅ Verify error pages work
// ✅ Check error logging

Key Takeaways

  • not-found.tsx - custom 404 pages, triggered by notFound()
  • error.tsx - error boundaries for route segments (Client Component)
  • global-error.tsx - catches root layout errors (includes html/body)
  • Nested error pages - more specific overrides less specific
  • reset() function - attempt to recover from errors
  • User-friendly messages - avoid technical jargon
  • Clear next steps - provide navigation and support links
  • Log errors - track issues for debugging

🎉 Middleware and Advanced Features Complete!

You've completed the Middleware and Advanced Features section! You've mastered:

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

You now have complete mastery of advanced Next.js features! You can intercept and modify requests with middleware, implement authentication flows, manage environment variables securely, use streaming and Suspense for progressive rendering, and create custom error pages for robust error handling. These advanced skills enable you to build production-ready applications with excellent user experiences and proper error handling!

🎓 Congratulations!

You've completed 62 comprehensive tutorials covering the complete Next.js 15 framework! From routing fundamentals to advanced middleware patterns, from styling to API development, from data fetching to error handling—you've learned it all. You're now equipped to build professional, production-ready Next.js applications!

Test Your Understanding

Question 1 of 4

What file creates a custom 404 page in Next.js?

Master error handling in Next.js! Learn custom 404 pages, error boundaries, and global error handling.

Previous
Streaming and Suspense
Next
Understanding Static and Dynamic Rendering

Continue Your Next.js Journey

Join 2,000+ developers mastering Next.js. More advanced tutorials coming soon - 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