Navigation

The left-hand nav and the prev/next links at the bottom of every page are both driven by a single JSON manifest. Group articles into chapters or list them flat, point the plugin at the file, and the nav, headings, and inter-page links fall out automatically.

The manifest

navigation.json (path configurable via options.navigation) takes one of two shapes. Most sites want the chaptered form:

{
  "chapters": [
    {
      "title": "Introduction",
      "articles": [
        { "slug": "",                "title": "Home" },
        { "slug": "getting-started", "title": "Getting started" }
      ]
    },
    {
      "title": "Authoring",
      "articles": [
        { "slug": "page-structure", "title": "Page structure" },
        { "slug": "code-blocks",    "title": "Code blocks" }
      ]
    }
  ]
}

Each chapter has a title (rendered as a small uppercase heading in the nav) and an articles array. A flat, chapter-less site can skip the wrapping and use an array directly:

[
  { "slug": "",        "title": "Home" },
  { "slug": "install", "title": "Install" },
  { "slug": "usage",   "title": "Usage" }
]

The chaptered shape is what the prev/next computation walks — it flattens the chapters in declared order and treats the whole thing as one sequence.

Slug-only entries

Inside an articles array each entry can also be a bare slug string. The nav loader resolves the title from the target page’s <h1>, so the manifest doesn’t repeat what the page already states:

{
  "chapters": [
    {
      "title": "Introduction",
      "articles": [ "", "getting-started" ]
    },
    {
      "title": "Authoring",
      "articles": [ "page-structure", "code-blocks" ]
    }
  ]
}

Bare strings and {slug, title} objects can be mixed in the same array — reach for an explicit title only when the nav label needs to differ from the page heading.

If a slug points at a file that doesn’t exist, or the file has no <h1>, the slug is used as the title and a warning is logged so the gap is visible in the build output.

Group dividers

A chapter can carry an optional section string: a divider label rendered above it in the nav that groups the chapters following it, up to the next chapter that declares its own section. It’s a flat marker, not an extra level of nesting — the prev/next sequence and chapter structure are unaffected.

{
  "chapters": [
    { "title": "Introduction", "articles": [ "", "getting-started" ] },
    {
      "section": "Guides",
      "title": "Authoring",
      "articles": [ "page-structure", "code-blocks" ]
    }
  ]
}

Article entries

slug

The URL path after the site root, without leading or trailing slashes. The empty string "" is the home page (mounted at /). A slug of "getting-started" maps to /getting-started/.

Slugs must match the directory structure produced by Eleventy. With the default contentDir: "src/content" setup, a file at src/content/foo/bar.html becomes the slug foo/bar.

title

The label shown in the nav. Optional — when omitted, the loader uses the target page’s <h1>. Set it explicitly when the nav label needs to differ from the page heading, e.g. nav reads “Install” even though the page itself is titled “Installing @carrotsearch/eleventy-apidocs”.

expand

Optional. Surfaces a page’s top-level <section id> entries as extra links in the nav, listed right after the article at the same level as ordinary pages — so a long reference page (a command list, an env-var list) reads like a chapter whose “pages” are its sections. Each targets /<slug>/#<id>. Pair it with a chapter of its own for the cleanest result.

Use true to expand the article’s own page, or a slug string to expand a different one:

{
  "chapters": [
    {
      "section": "Reference",
      "title": "Reference",
      "articles": [
        { "slug": "api-reference", "expand": true }
      ]
    }
  ]
}

Children hang off the article as a nested list; they are not separate steps in the prev/next sequence, and search already indexes every section independently, so expand changes the sidebar only.

How it renders

The left-hand <nav> walks the chapters in declared order, prints each title as a small uppercase heading, and lists each article as a link. The link to the current page gets an aria-current attribute and a styled background so visitors always know where they are.

Reordering the manifest reorders the nav. Renaming a chapter renames the heading. The data file is the single source of truth — the content directory is the source of pages, not of order.

Prev/next links

The footer of every article carries a two-column block: the previous article on the left, the next on the right. The sequence is the flattened article list, in manifest order — chapters are transparent for this purpose, so the last article of one chapter points to the first article of the next.

Articles at the ends of the sequence get a half-empty footer: the home page has only a “next”, the last article has only a “previous”. The grid keeps a fixed two-column layout regardless, so the remaining link doesn’t shift position.

What if a page isn’t in the manifest?

Pages whose slug doesn’t appear in navigation.json still build and are reachable by URL, but they show no prev/next block. Use this for pages you want to publish but keep out of the main reading sequence — release notes, error pages, deep-linked appendices.

Live reloads

The plugin watches the navigation file with addWatchTarget, so editing navigation.json during pnpm dev triggers a rebuild without restarting the server. The logo and footer fragments are watched the same way.