NetcaneTechnologies

Next.js

Error Handling in App Router: notFound(), error.tsx, loading.tsx

How to use notFound(), error boundaries, and loading UI so users get clear feedback instead of blank or broken pages.

Yasir Haleem3 min read

The App Router gives you notFound(), error.tsx, and loading.tsx to handle missing content, runtime errors, and loading states. Using them consistently keeps the experience predictable.

notFound() for missing content

When a resource doesn’t exist (e.g. invalid slug, deleted post), call notFound() from a server component or route handler. Next.js renders the nearest not-found.tsx up the segment tree and returns a 404. That’s better than rendering “Post not found” with a 200 status, which confuses users and crawlers.

// app/blog/[slug]/page.tsx
export default async function Post({ params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params;
  const post = await getPost(slug);
  if (!post) notFound();
  return <Article post={post} />;
}

Create a not-found.tsx in the segment where you want the 404 UI (e.g. app-level or under /blog). The root one is used if no segment defines its own.

error.tsx for runtime errors

An error.tsx file acts as an error boundary for that segment. When a server or client component in that segment throws, the error boundary catches it and shows the fallback. The component receives error and a reset function so you can offer “Try again.” error.tsx must be a client component (it uses hooks). Place it at the level you want to catch errors: e.g. one for the whole app or one per major section.

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

Log the error (e.g. to a service) in a useEffect or in the component. Don’t rely on error.tsx for expected cases like “not found”—use notFound() for that.

loading.tsx for pending UI

Add a loading.tsx in a segment to show a fallback while the segment’s layout and page are loading. Next.js wraps the segment in Suspense and shows loading.tsx until the content is ready. Use it for skeletons or spinners so the layout doesn’t jump and users see immediate feedback. You can have different loading UI per section (e.g. dashboard vs blog).

// app/dashboard/loading.tsx
export default function DashboardLoading() {
  return <DashboardSkeleton />;
}

Loading state is automatic for async server components and for client components that suspend; you don’t need to manually toggle a loading flag for the initial segment load.

Summary

Use notFound() when content is missing and rely on not-found.tsx for 404 UI. Use error.tsx for runtime errors and offer reset where it makes sense. Use loading.tsx for segment-level loading fallbacks. Together they give users clear, consistent handling for missing content, errors, and loading.

About the author

Yasir Haleem is founder and lead engineer at Netcane Technologies. He builds production Next.js sites with headless CMS platforms — Strapi, Contentful, Sanity, and WordPress — with a focus on performance, SEO, and maintainable architecture.

Let's work together

Tell us about your project. We respond within one business day with a clear scope, timeline, and estimate.