Lesson 1 of 81
In plain React, a filename is just a filename. In the App Router, a handful of exact filenames are instructions to the framework — get one wrong and the router silently ignores the file instead of erroring.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
Question 1 of 3
A folder `app/reports/` contains only a `loading.tsx` and no `page.tsx`. What happens when a user visits `/reports`?
Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
Plain React has one rule for components: write a function, export it, import it wherever you need it. The App Router (Next.js's app/ directory routing system) adds a second, parallel rule on top: certain exact filenames, placed inside a folder, tell the router what that folder's route segment should do — independent of anything you import or export by hand.
A folder becomes a URL segment just by existing. What actually renders for that segment depends on which of these special files you drop inside it:
page.tsx — makes the segment publicly reachable as a route and renders its main UI. No page.tsx, no URL.layout.tsx — shared UI that wraps this segment and everything nested under it. Persists across navigations; does not re-render or reset state when you move between child pages.loading.tsx — an automatic React Suspense fallback shown while this segment's content is still loading.error.tsx — an automatic React error boundary for this segment (must be a Client Component).not-found.tsx — the UI shown when notFound() is called or a route genuinely doesn't match.template.tsx — like a layout, but remounts fresh on every navigation instead of persisting.route.tsx — turns the segment into an API endpoint (GET/POST/etc.) instead of a page; mutually exclusive with page.tsx in the same segment./dashboard segment with four conventions at once// app/dashboard/layout.tsx — wraps every page under /dashboard, never re-renders on nav
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
return (
<section>
<DashboardNav />
{children}
</section>
);
}
// app/dashboard/loading.tsx — auto-wraps page.tsx in a <Suspense fallback={...}>
export default function Loading() {
return <DashboardSkeleton />;
}
// app/dashboard/error.tsx — auto-wraps page.tsx in an error boundary
"use client";
export default function Error({ reset }: { error: Error; reset: () => void }) {
return <button onClick={reset}>Try again</button>;
}
// app/dashboard/page.tsx — the actual content, makes /dashboard a real route
export default async function DashboardPage() {
const stats = await getStats();
return <Stats stats={stats} />;
}
Four files, zero imports connecting them to each other, and the router assembles all four into one working route: layout.tsx wraps everything, loading.tsx becomes the Suspense fallback while page.tsx's data fetch is in flight, and error.tsx catches anything that throws. Delete loading.tsx and nothing breaks — the segment just loses its automatic fallback and blocks on load instead. Rename page.tsx to Page.tsx or index.tsx and the route disappears entirely, because the router isn't scanning for "the default export in this folder," it's scanning for that one literal filename.
app/settings/ with no page.tsx inside it contributes to the route tree (nested layouts can still apply) but /settings itself 404s until a page.tsx exists there.layout.tsx and template.tsx look similar but behave oppositely on navigation. A layout persists and keeps component state; a template unmounts and remounts fresh every time you navigate within it, which resets state and re-runs effects.route.tsx and page.tsx cannot coexist in the same segment. A segment is either a page (renders UI) or a route handler (returns a Response), never both — this is enforced, not just a convention.loading.tsx or error.tsx that overrides the parent's for just that subtree, without touching the parent's files at all.not-found.tsx only catches notFound() calls and genuinely unmatched segments — it does not catch arbitrary thrown errors. Those are error.tsx's job."Walk me through what happens when someone requests /dashboard" is a real question that tests whether a candidate actually understands file-convention-based routing, or has only ever dragged components into a router config by hand — the App Router expects you to reason about composition from folder structure alone.