· By John Kavanagh

Understanding File‑System Routing in Next.js

Abstract image used to represent Understanding File‑System Routing in Next.js
Image by Krakograff Textures.

One of the reasons Next.js felt so refreshing when it arrived was that it removed a surprising amount of routing ceremony.

In many React setups, getting routing working meant pulling in a router library, defining route configuration, wiring components to paths, and keeping all of that in sync as the application grew. None of that was impossible, but it was another layer of work before the actual product even started feeling like a product.

Next.js took a different approach. Instead of treating routes as configuration first, it treated routes as files.

That is the basic idea behind file‑system routing.


Routes come from the pages directory

Next.js maps supported page files in pages to URLs. The examples here use ordinary .js page modules. Framework files have special roles: pages/_document.js, for example, customises the document shell rather than defining an ordinary content page.

For example:

pages/
  index.js
  about.js
  contact.js

creates:

  • /
  • /about
  • /contact

That mapping is direct enough that you can often understand a large part of an application's route structure simply by looking at the folder tree.


index files represent the root of a directory

This is the first small rule worth remembering.

pages/index.js maps to /.

If we create:

pages/blog/index.js

that maps to /blog.

So index means "the route for this directory itself", rather than adding /index to the URL.

This makes nested site sections feel natural to organise.


Nested Folders Create Nested URLs

Suppose the project grows:

pages/
  index.js
  blog/
    index.js
    first-post.js

Now the routes are:

  • /
  • /blog
  • /blog/first-post

This is where the system starts paying off. The route structure and the file structure reinforce each other, which usually makes both easier to reason about.


That Clarity Matters More than It First Appears

Routing problems are often not caused by the router being technically incapable. They are caused by the route structure being hard to see.

When a project has:

  • route definitions in one file
  • page components somewhere else
  • navigation links somewhere else again

the mental cost of change rises quickly.

File‑system routing reduces that cost by making the route tree more visible. It does not solve every routing concern, but it removes a surprising amount of indirection.


Each Page File Exports the Component for That Route

A minimal page looks like this:

import React from 'react';

const AboutPage = () => {
  return <h1>About</h1>;
};

export default AboutPage;

There is no separate registration step. The file location already does that job.

That is one of the reasons a new Next.js project can start feeling productive very quickly. Adding a page often means simply creating a file in the right place.


This Works Especially Well for Content‑Led Sites

For websites with a fairly clear page hierarchy, file‑system routing feels very natural:

  • home page
  • about page
  • contact page
  • services section
  • blog section

The folder structure can mirror the site structure directly. That makes the codebase easier to browse, especially for developers joining the project later.


The pages directory is not just about tidy URLs

It also helps establish an architectural boundary.

Ordinary page modules in pages are route entry points, so keep reusable components and utilities outside that route layer. Special framework files, such as _document.js, follow their own conventions.

That distinction encourages a useful separation between:

  • page‑level route files
  • reusable UI components
  • shared utilities

When a codebase respects that boundary, it tends to stay easier to navigate.


It is Still Possible to Overcomplicate the Structure

File‑system routing is simpler than many alternatives, but simplicity at the feature level does not prevent poor organisation.

For example, deeply nested folders can still become awkward if the product structure itself is awkward. Likewise, stuffing too much implementation detail directly into page files can make the route layer heavy and repetitive.

The routing model helps, but it does not replace sensible project structure.


Next.js Routing Removed the "Define It Twice" Problem

Before file‑based routing, there was often a mild duplication problem:

  • create a component
  • create a route definition
  • link them together

Next.js reduces that to one act:

  • create the page file

That may sound modest, but removing repeated setup work is often what makes a framework feel fast and pleasant rather than merely capable.


A Small Example of Growth

Imagine a company site expanding from three pages to a small article section:

pages/
  index.js
  services.js
  contact.js
  articles/
    index.js
    accessibility-basics.js
    seo-tips.js

The site structure is obvious immediately.

That is not just a convenience for the developer writing the code. It also helps code reviewers, future maintainers, and anyone trying to trace how the application is organised.


Keep the Route Boundary Clear

The useful team convention is simple: put route entry points in pages and give shared implementation code a separate home. A reviewer can then trace a URL to its page without treating every reusable component as another route.


Structure Does the Routing Work

File placement gives these ordinary pages a clear URL mapping. Keep the route tree readable, account for framework‑specific files, and let the page modules delegate their reusable work to components and helpers.

Postscript

May 2018: Next.js 6 added the custom _app.js extension point. Like _document.js, it has a framework role rather than representing an ordinary content page. This arrived after the original April article.

July 2019: Next.js 9 added built‑in TypeScript support and file‑based dynamic routes. The body keeps its earlier .js examples; later projects can use .tsx page files. Page discovery depends on the configured pageExtensions, so an arbitrary file under pages is not automatically a route. The separate Dynamic Routes in Next.js article covers those later route patterns.

March 2026: My later work on the Nando’s UK & Ireland Replatform gave me a larger example of the same practical benefit: file‑system routing helped keep the Next.js codebase legible without introducing a separate routing configuration problem. That project belongs to the later work, rather than the original 2018 context.

Planning a platform change?

I help teams make difficult platform work clearer, from architecture decisions and migrations to launch recovery, performance, and search visibility.