React & Next.js

The Next.js App Router — file-based routing

Plain React has no built-in concept of "pages" or URLs at all — that's a separate library's job. Next.js's App Router bakes routing directly into the filesystem itself: the folder structure under app/ IS the URL structure, no route-configuration file to keep in sync.

Intermediate

3 min read

Plain React has no router at all

// Plain React: rendering different "pages" means manually swapping
// what's rendered based on some piece of state, with no real URL involved
function App() {
  const [page, setPage] = useState("home");
  if (page === "about") return <AboutPage />;
  return <HomePage />;
}

React itself is a UI library, not a framework — it has no built-in notion of a "page," a URL, or navigation at all. Real multi-page apps built with plain React need a separate routing library (React Router being the most common) layered on top, which maps URL paths to which components render. Next.js bundles this in directly, and specifically the App Router (the app/ directory convention, as opposed to the older pages/ directory) does it through the filesystem itself.

The core idea: folder structure IS URL structure

app/
  page.tsx              -> /
  about/
    page.tsx             -> /about
  blog/
    page.tsx              -> /blog
    [slug]/
      page.tsx              -> /blog/:slug (a dynamic segment)

Every page.tsx file inside app/ becomes a real, routable URL, and the route it maps to is exactly the folder path leading to it — no separate routes.js config file listing every path and which component handles it, the kind every React Router setup requires. [slug] (square brackets around a folder name) is a dynamic segment — it matches any value at that position in the URL, and that matched value is available to the page as a route parameter.

layout.tsx: shared UI that wraps a whole subtree, without re-rendering on navigation

// app/blog/layout.tsx
export default function BlogLayout({ children }) {
  return (
    <div>
      <BlogSidebar />
      {children} {/* the specific page.tsx for the current route renders here */}
    </div>
  );
}

A layout.tsx file wraps every page.tsx beneath it in the folder tree — app/blog/layout.tsx wraps every page under blog/, rendering shared UI (a sidebar, a header) exactly once, with children being whichever specific page is actually active. Critically, navigating between two pages that share a layout does not re-render that shared layout — only the changed part of the tree updates, which is both a real performance benefit and the direct mechanism behind persistent UI (like audio that keeps playing, or a sidebar that doesn't flicker) across route changes within the same layout.

Special files: reserved names with specific, automatic jobs

app/
  loading.tsx    - shown automatically while this route segment's data is loading
  error.tsx       - shown automatically if this route segment throws during render
  not-found.tsx    - shown when notFound() is called, or no matching route exists

Beyond page.tsx and layout.tsx, the App Router reserves a handful of specific filenames, each with an automatic, built-in job — loading.tsx is shown while an async page is still resolving (no manual loading-state management required, covered further in the data-fetching lesson), error.tsx automatically catches a render-time error in that segment and shows a fallback instead of the whole app crashing, not-found.tsx handles both explicit notFound() calls and routes that simply don't exist. This is meaningfully different from plain React Router, where loading states and error boundaries are something you wire up yourself, file by file — here, the filesystem convention wires it up automatically.

Route groups: organizing folders without affecting the actual URL

app/
  (marketing)/
    page.tsx           -> / (the parentheses folder is invisible to the URL)
    about/page.tsx       -> /about
  (app)/
    dashboard/page.tsx     -> /dashboard
    settings/page.tsx       -> /settings

A folder name wrapped in parentheses, (marketing), is a route group — it groups related routes together for organizational purposes (often to apply a different shared layout.tsx to just that group, like a public marketing layout vs. an authenticated-app layout) without that folder name appearing anywhere in the actual URL. (marketing)/about/page.tsx still maps to /about, not /marketing/about — the parentheses are a pure filesystem-organization tool, invisible to routing itself.

Further reading

Check your understanding

A quick comprehension check — not tracked, not graded, just for you.

1. How does the Next.js App Router determine what URL a page maps to?

2. What does a layout.tsx file do, and why doesn't it re-render on navigation between pages that share it?

3. What does wrapping a folder name in parentheses, like (marketing), actually do?