Error Handling in Next.js

The Next.js App Router uses the file system for error handling. The error.tsx and not-found.tsx files automatically create Error Boundaries at each route segment level.

error.tsx

The error.tsx file catches runtime errors and shows fallback UI instead of a broken page:

'use client'

export default function Error({
  error,
  reset
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  return (
    <div>
      <h2>Something went wrong</h2>
      <p>{error.message}</p>
      <button onClick={() => reset()}>Try again</button>
    </div>
  )
}

Key points:

  • error.tsx must be a client component ('use client')
  • reset attempts to re-render the segment
  • error.digest contains an error hash for server-side logging

Nested Error Handling

Errors bubble up to the nearest error.tsx:

An error in /problems/123 will be caught by app/problems/error.tsx. If it does not exist, it bubbles up to app/error.tsx.

Important:

error.tsx does not catch errors in the layout.tsx of the same segment because the error boundary is nested inside the layout. To handle layout errors you need error.tsx in the parent segment.

global-error.tsx

To handle errors in the root layout:

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

export default function GlobalError({
  error,
  reset
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  return (
    <html>
      <body>
        <h2>A critical error occurred</h2>
        <button onClick={() => reset()}>Reload</button>
      </body>
    </html>
  )
}

global-error.tsx replaces the entire root layout, so it must contain <html> and <body>.

not-found.tsx

To handle 404 errors:

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

export default function NotFound() {
  return (
    <div>
      <h2>Page Not Found</h2>
      <p>We could not find the requested page on Hack Frontend.</p>
      <Link href="/">Return to homepage</Link>
    </div>
  )
}

not-found.tsx triggers automatically when Next.js cannot find a route. It can also be called manually:

import { notFound } from 'next/navigation'

export default async function DocPage({
  params
}: {
  params: { slug: string }
}) {
  const doc = await getDoc(params.slug)

  if (!doc) {
    notFound()
  }

  return <article>{doc.content}</article>
}

Handling Order

layout.tsx
  error.tsx ← catches errors from:
    loading.tsx
      not-found.tsx
        page.tsx ← errors here bubble up to error.tsx

Error Handling in Server Actions

Server Actions return errors via return, not throw:

'use server'

export async function updateProfile(formData: FormData) {
  try {
    await db.user.update({ ... })
    revalidatePath('/settings')
    return { success: true }
  } catch (error) {
    return { error: 'Failed to update profile' }
  }
}

Useful Resources