Quiz - File-system routing with the App Router
A bare revenue-chart.tsx next to your page.tsx is already non-routable — Next.js never turns a stray component file into a URL. So why does the course still insist you tuck co-located code under a _components/ or _lib/ folder?
default.tsx or template.tsx would silently become a framework convention — a _-prefixed folder is off the router’s radar entirely, so it can never collide./dashboard/revenue-chart until you prefix it.@/ import alias only resolves files that live inside an underscore-prefixed folder.default.tsx or template.tsx you wrote as a plain component would quietly turn into a parallel-route fallback or a remounting template. A whole _-prefixed folder is invisible to the router, so nothing inside it can ever collide with a framework name. A stray file does not become a route (only page.tsx/route.ts do), and the @/ alias has nothing to do with private folders.A dashboard’s sidebar — including its collapsed/expanded toggle and scroll position — lives in a Client Component inside app/(app)/layout.tsx. The page itself holds a half-typed form. The user navigates from /dashboard to /dashboard/settings. What survives the navigation? Select all that apply.
An invoice detail page sits at app/invoices/[id]/page.tsx, and ids are UUIDs. A senior reviewer rejects a version that does const invoice = await getInvoice((await params).id) directly. What’s the load-bearing fix?
safeParse the awaited params against z.object({ id: z.uuid() }), call notFound() on failure, and only pass parsed.data.id to the query — the URL is untrusted input.try/catch so a malformed id is caught and rendered as an error page instead of crashing.Number(params.id) before querying so a non-UUID string can’t reach the database.params.id is whatever the user typed in the address bar — untrusted input — so the reflex is capture → validate → query: parse it against a schema (z.uuid() for the course’s UUIDs) and notFound() on a miss, so a garbage or hostile id never touches the query. A try/catch around the query validates after the bad value already hit the database, defeating the point — and it would also swallow the notFound() signal. Number() on a non-numeric string yields NaN silently rather than rejecting.A Server Component runs if (!session) redirect('/sign-in'), then return <Dashboard userId={session.userId} />. A teammate wraps the whole thing in a try/catch “to be safe.” What goes wrong?
catch swallows the redirect signal — redirect() works by throwing, so a broad catch eats it and the protected page renders for a signed-out user instead of bouncing them.redirect() in try/catch is the recommended way to handle the no-session branch cleanly.return line throws because session is null, so you must assign const result = redirect(...) and return that instead.redirect() (like notFound() and permanentRedirect()) is typed never: it throws a signal the framework is waiting to catch, and a broad try/catch catches that signal like any other throw, so the reroute silently dies. The rule is absolute — never wrap the throwing trio in try/catch. And because the throw exits the function, the return is unreachable on the no-session path and TypeScript narrows session to non-null below the if; there is no value to assign.You build a list-plus-detail invoices screen with a @detail slot and ship it. Clicking around in dev works flawlessly, but a user who opens a shared /invoices/42 link in a fresh tab gets a full-page 404. What did you forget, and why did dev never catch it?
default.tsx in the @detail slot. Soft navigation carries the previous slot match forward, but a hard load resolves every slot from the URL — an unmatched slot with no fallback 404s the whole route, and dev only ever soft-navigates.loading.tsx in the @detail slot — without it the slot can’t stream on a fresh load and the route times out into a 404.@detail folder should have been named (detail) — the @ prefix isn’t a valid route group, so it 404s on direct visits.default.tsx, the framework 404s the entire route rather than render half a screen. Clicking around in dev is all soft navigation, which keeps the unchanged slot’s match — so the bug hides until a real user hard-loads a link. loading.tsx is about streaming, not the unmatched case, and @detail is correctly a slot (a named prop), not a route group.A dashboard nests everything under a (dashboard) route group. Settings lives at app/(dashboard)/settings/page.tsx (URL /settings) and members at app/(dashboard)/members/page.tsx (URL /members). You add an intercepter at app/(dashboard)/settings/@panel/‹prefix›members/[id]/page.tsx so clicking a row in settings opens the member view as a panel over it. Which prefix is correct?
(..) — the count is URL-segment-relative, and both @panel (a slot) and (dashboard) (a route group) contribute no URL segment, so /settings and /members are just one segment apart.(..)(..) — walk two folders up the disk tree, past @panel and settings, to reach the members route.(...) — root the count at the (dashboard) group, since everything lives under it.@panel (a slot) and (dashboard) (a route group), and neither adds a URL segment — strip them away and /settings and /members are siblings, one segment apart, so (..) is right. (..)(..) and (...) count disk folders, which is exactly the trap; counting folders makes the intercepter silently never fire.Quiz complete
Score by topic