Use loading.tsx for route loading states, error.tsx for error boundaries, and Suspense for component-level loading with streaming.
Next.js App Router has built-in conventions for handling loading and error states elegantly.
loading.tsx in any route folder shows automatically while that route segment loads. It wraps the page in a Suspense boundary, so users see instant feedback instead of a blank screen. Create skeleton UIs or spinners here.
error.tsx creates an error boundary for that route segment. It's a Client Component that receives error and reset props. Display a user-friendly error message and optionally let users retry. Errors don't crash the whole app—only the affected segment.
not-found.tsx handles 404s. Call notFound() from your page or layout when content doesn't exist, and this component renders. Customize per-section of your app.
Suspense boundaries provide component-level loading states. Wrap async components in <Suspense fallback={<Loading />}> to show loading UI while they fetch data. This enables streaming—the page sends immediately, and components pop in as they're ready.
Streaming with Suspense improves Time to First Byte. The shell renders instantly, loading states show, and content streams in progressively. Users see structure immediately, then content fills in.
Combine these: loading.tsx for page-level loading, Suspense for component-level streaming, error.tsx for error recovery, and not-found.tsx for missing content. Each level handles its own states.
Use loading.tsx for route loading states, error.tsx for error boundaries, and Suspense for component-level loading with streaming.
Join our network of elite AI-native engineers.