Special Router Files
Next.js provides built-in special file conventions to handle loading states, system crashes, and missing pages automatically without manual React state tracking.
1. Async Loading States (loading.tsx)
Next.js leverages React Suspense. By creating a loading.tsx file inside a route directory, Next.js automatically wraps the route page inside a Suspense boundary.
While the page is fetching data on the server, the loading layout displays instantly:
// src/app/dashboard/loading.tsx
export default function DashboardLoading() {
return (
<div className="flex items-center justify-center h-full">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900"></div>
<span className="ml-2">Loading dashboard metrics...</span>
</div>
);
}Once the page finishes fetching data, Next.js replaces the loading view with the actual page component.
2. Catching Runtime Crashes (error.tsx)
If a component crashes during rendering or data fetching, Next.js catches the error and renders the interface defined in error.tsx instead of crashing the entire site.
Because error handlers must respond to client-side actions and recovery button clicks, error.tsx must be a Client Component:
// src/app/dashboard/error.tsx
"use client";
import { useEffect } from "react";
interface ErrorProps {
error: Error & { digest?: string };
reset: () => void;
}
export default function DashboardError({ error, reset }: ErrorProps) {
useEffect(() => {
// Log error details to an external logging service
console.error("Dashboard error occurred:", error);
}, [error]);
return (
<div className="p-8 border border-red-200 rounded-lg bg-red-50 text-center">
<h2 className="text-xl font-bold text-red-600">Something went wrong!</h2>
<p className="mt-2 text-gray-600">{error.message}</p>
<button
onClick={() => reset()} // Attempt to recover by re-rendering the route segment
className="mt-4 px-4 bg-red-600 text-white rounded hover:bg-red-700"
>
Try Again
</button>
</div>
);
}3. Custom 404 Pages (not-found.tsx)
When a resource does not exist, or when you trigger the notFound() function manually from a route component, Next.js renders the nearest not-found.tsx file in the directory hierarchy:
// src/app/not-found.tsx
import Link from "next/link";
export default function GlobalNotFound() {
return (
<div className="flex flex-col items-center justify-center min-h-screen">
<h1 className="text-6xl font-bold">404</h1>
<h2 className="text-2xl mt-4">Page Not Found</h2>
<p className="mt-2 text-gray-500">The requested resource could not be found.</p>
<Link href="/" className="mt-6 text-blue-600 hover:underline">
Return Home
</Link>
</div>
);
}