When a user clicks a link and nothing happens for a couple of silent seconds, it makes the app feel broken. Distinguishing between loading, an expected not-found, and an unexpected error makes the app feel a lot more trustworthy.
The key idea
loading.tsx is the fallback UI for a route segment, giving instant feedback during navigation. notFound() routes an expected state, like missing data, to the not-found.tsx UI, treating it as a proper 404. error.tsx catches unexpected exceptions with an error boundary and lets the user retry. Because the error component needs interactivity, it has to be a Client Component.
Let's try it together
// app/notes/loading.tsx
export default function Loading() {
return <p>မှတ်စုများ ဖွင့်နေသည်…</p>;
}
// app/notes/error.tsx
"use client";
export default function ErrorPage({
unstable_retry,
}: {
error: Error;
unstable_retry: () => void;
}) {
return <button onClick={unstable_retry}>ပြန်စမ်းကြည့်မယ်</button>;
}
// app/notes/not-found.tsx
export default function NotFound() {
return <h2>ဒီမှတ်စုကို မတွေ့ပါ</h2>;
}How the code works
Put all three files in the same route folder. Loading shows up while waiting on data, not-found shows up when a record doesn't exist, and error shows up when an exception occurs. Don't lump all of them together under one generic "something went wrong" message.
The right UI appears for every state — loading, a retryable error, and 404.5-Minute Try-It
Write two loading skeleton cards, and add text the user can understand plus a link back Home in the error UI.
Next.js — Error Handling — Next.js