Skip to the solution
MediumFrontend8 min read

How do you show navigation loading feedback with useLinkStatus in Next.js?

589 views

The problem

Question presented to candidate: "Users click a link in our sidebar and nothing seems to happen for a second before the next page appears. How would you give them feedback in Next.js, and where does useLinkStatus fit?"

What a strong answer should cover:

  • Why the delay exists: the destination is dynamic, not prefetched, and has no loading.js, so the router waits for the server before changing the URL.
  • useLinkStatus() from next/link returns { pending } for the nearest parent <Link>; it must be called in a Client Component rendered inside that Link.
  • pending is true from the click until the history updates; it is the inline, per-link signal, while loading.js is the route-level fallback.
  • Fix the cause first: a loading.js file or prefetching makes the transition instant; useLinkStatus is the patch for the remaining slow cases.
  • UX details: a fixed-size, always-rendered hint (no layout shift) and a short animation delay so fast navigations do not flash.

Clarifying questions expected:

  • "Is the slow route dynamic, and does it have a loading.js?" β€” that decides whether a route fallback already solves it.
  • "Is prefetching disabled on these links?" β€” useLinkStatus matters most with prefetch={false}.

Code / implementation expected: Yes β€” a small Client Component using useLinkStatus inside a <Link>, plus the CSS idea for a delayed fade-in.

  • nextjs
  • navigation
  • loading-ui
  • hooks

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

8 min read Β· 8 sections

Read the code

A per-link pending hint
// app/nav/hint.tsx
"use client";
import { useLinkStatus } from "next/link";

export default function Hint({ id }: { id: string }) {
  const { pending } = useLinkStatus();
  // Always rendered and fixed size, so toggling it never shifts layout.
  return <span data-hint={id} data-pending={String(pending)} aria-hidden className={pending ? "link-hint is-pending" : "link-hint"} />;
}

// app/nav/layout.tsx
import "./hint.css";
import Link from "next/link";
import Hint from "./hint";

export default function NavLayout({ children }: { children: React.ReactNode }) {
  return (
    <div>
      <nav>
        <Link href="/nav/slow" prefetch={false} id="to-slow">Slow (no loading.js) <Hint id="slow" /></Link>{" | "}
        <Link href="/nav/with-loading" prefetch={false} id="to-loading">Slow with loading.js <Hint id="loading" /></Link>{" | "}
        <Link href="/nav/fast" id="to-fast">Static, prefetched <Hint id="fast" /></Link>
      </nav>
      {children}
    </div>
  );
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 64 of 129 in Next.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Next.js questions
Up nextMedium

What is on-demand revalidation in Next.js (revalidatePath / revalidateTag)?

Continue