Skip to the solution
HardFrontend9 min read

What are parallel routes (@slot) in Next.js and when would you use them?

783 views

The problem

Question presented to candidate: "Our dashboard shows a team panel, a revenue chart and an activity feed side by side. Each one loads at its own speed and should fail on its own. How would you build that with the App Router, and what happens when the user reloads a deep URL?"

What a strong answer should cover:

  • A folder named @name is a slot: the parent layout receives it as a prop (team, analytics) next to children, which is itself the implicit slot. Slots are not URL segments.
  • Each slot is its own route subtree with its own loading.tsx and error.tsx, so slots stream and fail independently.
  • On a soft navigation, slots that have no match for the new URL keep showing their previous page; on a hard navigation (reload, direct visit) they render default.tsx.
  • Every slot needs a default.tsx for URLs it does not match; in Next.js 16 the build fails without one.
  • Use cases: dashboards, conditional UI by role (with authorization inside each slot, because all slots run), tabbed sub-navigation inside a slot, and modals together with intercepting routes.
  • All slots at one level share a render mode: one dynamic slot makes the route dynamic.

Clarifying questions expected:

  • "Do the panels change with the URL, or are they always the same?" β€” if they never navigate, plain components with Suspense are simpler.
  • "Is any panel role-specific?" β€” conditional slots still execute, so authorization must live inside the slot.

Code / implementation expected: Yes β€” a dashboard layout with @team, @revenue and role slots, each with default.tsx, and a per-slot loading.tsx.

  • nextjs
  • parallel-routes
  • routing
  • advanced

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

9 min read Β· 8 sections

Read the code

Dashboard with independent slots and role-based panels
// app/dashboard/layout.tsx
import { getRole } from "../lib/auth";

// Every @folder below becomes a prop of this layout. children is the implicit slot (page.tsx).
export default async function DashboardLayout({ children, team, revenue, admin, user }: LayoutProps<"/dashboard">) {
  const role = await getRole();
  return (
    <div className="grid">
      <main>{children}</main>
      <aside>{team}</aside>
      <section>{revenue}</section>
      {/* Both role slots render on the server; this only picks which one is shown. */}
      {role === "admin" ? admin : user}
    </div>
  );
}

// app/dashboard/default.tsx
// Needed for children when a sibling slot has a deeper page (/dashboard/members) and the browser reloads.
export default function DashboardDefault() { return null; }

// app/dashboard/@team/page.tsx
import Link from "next/link";
export default function TeamSummary() {
  return <Link href="/dashboard/members">See all members</Link>;
}

// app/dashboard/@team/default.tsx
export default function TeamDefault() { return null; }

// app/dashboard/@revenue/page.tsx
import { connection } from "next/server";

// A slow slot: its own loading.tsx streams first, the rest of the dashboard is not blocked.
export default async function Revenue() {
  await connection();
  const total = await new Promise<number>((r) => setTimeout(() => r(42_000), 1200));
  return <p>Revenue: ${total}</p>;
}

// app/dashboard/@revenue/loading.tsx
export default function RevenueLoading() { return <p>Loading revenue...</p>; }

// app/dashboard/@revenue/error.tsx
"use client";
export default function RevenueError() { return <p>Revenue is unavailable.</p>; }

// app/dashboard/@revenue/default.tsx
// Shown for this slot after a reload on /dashboard/members, where the slot has no page.
export default function RevenueDefault() { return <p>Revenue</p>; }

// app/dashboard/@admin/page.tsx
import { getRole } from "../../lib/auth";

// Authorize inside the slot too: the layout condition hides it, but the slot still runs.
export default async function AdminPanel() {
  if ((await getRole()) !== "admin") return null;
  return <p>Admin tools</p>;
}

// app/dashboard/@admin/default.tsx
export default function AdminDefault() { return null; }

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 109 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 nextHard

How do you handle authentication in Next.js?

Continue