100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
JavaScript

Loading and Error States

Learn how loading.js, error.js, and not-found.js use React Suspense and Error Boundaries to give every route automatic, granular loading and error UI.

Navigation & UIIntermediate9 min readJul 10, 2026
Analogies

loading.js and Automatic Suspense Boundaries

When you add a loading.tsx file next to a page.tsx in the App Router, Next.js automatically wraps that route segment's content in a React Suspense boundary and shows the loading.tsx component as the fallback while the page's Server Component is fetching data. This means you get instant loading UI, like a skeleton screen or spinner, without manually writing any Suspense JSX yourself, because the file-based convention wires it up for you. The loading state also enables streaming: the shared layout and any static parts of the page can be sent to the browser immediately while slower, dynamic content continues rendering on the server behind the scenes.

🏏

Cricket analogy: loading.tsx is like the 'players walking out' graphic broadcasters show while ground staff finish covering the pitch after rain, keeping viewers engaged instead of a blank screen during the delay.

error.js and Error Boundaries

An error.tsx file defines a React Error Boundary for its route segment, automatically catching runtime errors thrown during rendering, including errors from async Server Components, and displaying a fallback UI instead of crashing the whole app. Because error.tsx renders on the client and needs interactivity, it must start with the 'use client' directive, and Next.js passes it two props: error (the thrown Error object, possibly including a digest for server-logged errors) and reset, a function that attempts to re-render the segment from scratch. This lets you build a 'Try again' button that re-runs the failed data fetch without forcing the user to do a full page reload.

🏏

Cricket analogy: error.tsx's reset function is like a no-ball being called and the batter getting a free hit replay of that delivery, a clean retry instead of the whole innings being scrapped.

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 render error:', error.digest ?? error.message);
  }, [error]);

  return (
    <div role="alert">
      <h2>Something went wrong loading your dashboard.</h2>
      <button onClick={() => reset()}>Try again</button>
    </div>
  );
}

Not Found Handling with not-found.js

A not-found.tsx file defines the UI shown when a route can't be matched, or when code explicitly calls the notFound() function imported from next/navigation, such as when a database lookup for a dynamic [slug] segment returns nothing. Calling notFound() throws a special internal signal that Next.js catches to render the nearest not-found.tsx boundary and respond with a 404 HTTP status code, distinguishing it from a thrown Error which would instead be caught by error.tsx. A global app/not-found.tsx acts as the catch-all fallback for any unmatched route in the entire application, while nested not-found.tsx files can provide more specific 404 pages for a particular route segment.

🏏

Cricket analogy: notFound() is like an umpire correctly ruling 'not out' with certainty on a clean bat-pad decision, a definite, expected outcome, distinct from a rain-delay error that halts play unexpectedly.

Granular Loading with Suspense

Beyond the file-based loading.tsx, you can wrap individual slow components in your own <Suspense fallback={...}> boundaries to stream in parts of a page independently, which is especially powerful when one section, like a personalized recommendations widget, is much slower than the rest of the page. Because React Server Components can be async, awaiting a slow fetch inside a component wrapped in Suspense lets the rest of the page render and reach the browser immediately, with only that component's fallback showing until its data resolves. This pattern, often combined with streaming, means a page's static header and product details can appear instantly while a slower reviews section streams in a moment later.

🏏

Cricket analogy: Granular Suspense boundaries are like a broadcast showing the main match feed live while a separate 'DRS review in progress' box loads independently in the corner, without freezing the whole broadcast.

Streaming with Suspense requires the server to support HTTP streaming responses. On most modern hosting platforms, including Vercel and any Node.js server running next start, this works out of the box; some edge or serverless environments with strict buffering can affect how progressively the stream is flushed to the client.

error.tsx cannot catch errors thrown in the layout.tsx that wraps it, because the error boundary is rendered inside that layout, not around it. To catch errors originating in a root layout, you need a special global-error.tsx file at the top of the app directory, which must also define its own <html> and <body> tags since it replaces the root layout entirely when active.

  • loading.tsx automatically wraps its route segment in a Suspense boundary, showing instant fallback UI during data fetching.
  • error.tsx defines a Client Component Error Boundary that catches rendering errors and receives error and reset props.
  • The reset function attempts to re-render the failed segment without a full page reload.
  • notFound() from next/navigation renders the nearest not-found.tsx and returns an HTTP 404 status.
  • Nested not-found.tsx files can override the global app/not-found.tsx for specific route segments.
  • Custom <Suspense> boundaries around individual async components enable granular, independent streaming.
  • global-error.tsx is required to catch errors thrown in the root layout itself, and must define its own html and body tags.

Practice what you learned

Was this page helpful?

Topics covered

#JavaScript#NextJsStudyNotes#WebDevelopment#LoadingAndErrorStates#Loading#Error#States#Automatic#StudyNotes#SkillVeris