---
title: Navigation
description: Blume builds the sidebar from your files, then lets you refine it with frontmatter, folder meta, or config — breadcrumbs and outlines follow along.
---

Blume builds your sidebar from the file system, then lets you refine it as much — or as little — as you want: page by page, folder by folder, or with one explicit config. Breadcrumbs, previous/next links, and the on-page outline all follow from the same model, with nothing to wire up.

## The generated sidebar

By default the sidebar mirrors your content tree:

- folders become **groups**, files become **pages**
- a page's label is its frontmatter `title`; a group's label is the humanized folder name
- items sort by [numeric prefix](/docs/content), then alphabetically, and a folder's `index` page comes first

That's enough for many sites — everything below is opt-in.

## Page label, icon, and badge

Tune how a single page appears in the sidebar from its frontmatter, under `sidebar`:

```yaml lineNumbers
sidebar:
  label: Quickstart # override the title in the sidebar
  icon: rocket # an icon from Blume's built-in set
  badge: New # a small label beside the entry
  order: 1 # sort position within its group
```

See [Frontmatter](/docs/reference/frontmatter) for the full page schema.

## Folder groups

Each folder becomes a sidebar group. Drop a [`meta.ts`](/docs/content/meta) beside its pages to set the group's title, icon, order, and the order of its children:

```ts meta.ts
import { defineMeta } from "blume";

export default defineMeta({
  title: "Guides",
  icon: "book-open",
  pages: ["configuration", "theming", "deployment"],
});
```

See [Folder meta](/docs/content/meta) for every field and computing meta at scan time.

A folder's `meta.title` and its own `index` page's frontmatter `title` are resolved independently — translating one under i18n and forgetting the other renders a correct sidebar with a stale `<title>`/heading on the landing page itself. Blume reports a `BLUME_NAV_INDEX_TITLE_MISMATCH` warning when they diverge. Untranslated pages filled in from the fallback locale are exempt — their title belongs to the fallback locale, and the fix is translating the page, not editing its frontmatter.

To group pages _without_ adding a URL segment, use a parenthesized folder name — see [Pages](/docs/content#group-folders).

## Display modes

`navigation.sidebar.display` sets how every sidebar group renders:

```ts blume.config.ts lineNumbers
navigation: {
  sidebar: {
    display: "flat", // "flat" | "group" | "page"
  },
}
```

- **`flat`** (default) — a non-collapsible header with its pages listed beneath. Pages that aren't in any group always list first, above the group sections, so they can't be mistaken for a group's children.
- **`group`** — a collapsible `<details>` disclosure per group. Groups start collapsed by default; a group containing the current page always starts open, so only the section you're in is expanded. Set `collapsed: false` in [folder meta](/docs/content/meta) to force a group open regardless.
- **`page`** — each group is a single row that, when clicked, slides the sidebar into a sub-panel showing only that group's items, with a back arrow at the top. The panel is route-aware, so landing directly on a page inside the group opens straight to it.

:::tip
`page` mode keeps deep sections tidy — reach for it when groups have many children and you'd rather drill into them than scroll past them.
:::

### Per-group overrides

Any generated group can opt out of the global mode — no explicit sidebar required. Set `display` in the folder's [`meta.ts`](/docs/content/meta), or — when the folder has an `index` page — under `sidebar` in that page's frontmatter, and only that group changes:

```ts meta.ts
import { defineMeta } from "blume";

export default defineMeta({
  title: "Client SDKs",
  display: "page",
});
```

```yaml index.mdx
---
title: Client SDKs
sidebar:
  display: page
---
```

A generated group's effective mode resolves highest priority first:

1. `sidebar.display` in the group's own `index` page frontmatter
2. `display` in the folder's `meta.ts`
3. The global `navigation.sidebar.display`
4. The Blume default (`flat`)

A group's `display` applies to that group only — nested subgroups resolve their own value through the same chain. A `page`-mode group with an index page still drills into its sub-panel: the index page lists as the panel's first item, and landing on its URL opens the panel directly.

`sidebar.display` means nothing anywhere else — on a non-index page, on the content root's own `index` page (the root isn't a group; use `navigation.sidebar.display`), or on any page when an [explicit sidebar](#explicit-sidebar) is configured (its items own each group's mode) — so Blume reports a `BLUME_SIDEBAR_DISPLAY_IGNORED` warning instead of silently dropping it. `collapsed` remains specific to `group` mode; it's inert when a group resolves to `flat` or `page`.

A group in an [explicit sidebar](#explicit-sidebar) overrides the global mode with its own `display`, exactly as before.

## Ordering

When the sidebar is generated, order is resolved highest priority first:

<Steps>
  <Step title="Config sidebar">
    An explicit `navigation.sidebar` replaces the generated tree entirely.
  </Step>
  <Step title="Folder meta">
    The `pages` array in `meta.ts` orders a group.
  </Step>
  <Step title="Frontmatter">`sidebar.order` on a page.</Step>
  <Step title="File system">
    An `index` page first, then numeric prefixes, then alphabetical by label.
  </Step>
</Steps>

Two siblings that land on the same explicit or numeric order fall back to alphabetical order between themselves — Blume reports a `BLUME_DUPLICATE_SIDEBAR_ORDER` warning so the tie doesn't go unnoticed.

## Hidden pages

Hide a page from the sidebar — and from previous/next pagination — while keeping it built and reachable by its URL:

```yaml
sidebar:
  hidden: true
```

## Tabs

Render top-level sections as tabs in the header, useful for splitting a large site into distinct areas — say adapters, an API, and AI guides. A tab is highlighted when the current route falls under its `path`:

```ts blume.config.ts lineNumbers
navigation: {
  tabs: [
    { label: "Adapters", path: "/adapters", icon: "plug" },
    { label: "API", path: "/api", icon: "rocket" },
    { label: "AI", path: "/ai", icon: "sparkles" },
  ],
}
```

An enabled [OpenAPI or AsyncAPI reference](/docs/advanced/api-reference) mounts at its route but doesn't add a tab on its own — point a tab at that route to surface it in the header (and, for the native renderer, to scope its operations sidebar), with whatever label you like:

```ts blume.config.ts
navigation: {
  tabs: [
    { label: "API", path: "/reference" },
  ],
}
```

A tab's `path` is its section prefix, and it doubles as the link target. A section whose `path` isn't a page of its own — a folder with no `index.mdx` — would link to a 404, so the tab falls back to the first page in the section instead. Set `href` when you want it to land somewhere else:

```ts blume.config.ts
navigation: {
  tabs: [
    { label: "Changelog", path: "/changelog", href: "/changelog" },
  ],
}
```

This matters for routes that aren't part of the content tree, since the fallback can't see them: the generated [changelog](/docs/advanced/changelog) index, or a [custom page](/docs/advanced/custom-pages) you added under `pages/`. Without `href`, a `/changelog` tab lands on the newest entry rather than the index. Tabs that don't set `href` are unaffected.

On an [i18n](/docs/content/i18n) site, a tab's `label` (and a dropdown item's) can be a per-locale map instead of a string — the active locale's entry wins, then the default locale's:

```ts blume.config.ts
navigation: {
  tabs: [
    { label: { en: "Docs", fr: "Documentation" }, path: "/docs" },
    { label: "CLI", path: "/cli" }, // a plain string renders as-is everywhere
  ],
}
```

Tabs also **scope the sidebar**: when the current route falls under a tab's `path`, the sidebar shows only that section's pages — so `/adapters/*` lists the adapters and nothing else. The folder at a tab's `path` becomes the section, so this needs no extra config beyond the tabs themselves; structure your content into a folder per tab and point each tab at it.

On a route under no tab (or a tab whose `path` is `/`), the sidebar shows the pages that _don't_ belong to a tab — each tab's folder is hidden from it, since that section already has its own tab in the header. So a root landing page lists your loose top-level pages while the sectioned content stays behind its tab, mirroring Fumadocs' root folders. If a route has no pages of its own to show this way, the full tree is shown instead, so the sidebar is never left blank.

## Selectors

For switching between whole partitions of a site — a product, a version, or any grouped set of destinations — add a `selector`. Each renders as a dropdown in the header, showing the option whose `path` matches the current route:

```ts blume.config.ts lineNumbers
navigation: {
  selectors: [
    {
      kind: "version",
      label: "Version",
      items: [
        { label: "v2 (latest)", path: "/v2", icon: "rocket" },
        { label: "v1", path: "/v1" },
      ],
    },
  ],
}
```

Each item takes a `label`, a `path`, and optional `icon`, `description`, and `tag`. `kind` (`dropdown`, `product`, `version`, or `language`) is a hint for how the selector is used; all render the same dropdown.

With [versioning](/docs/content/versioning) configured, Blume renders a version selector automatically — declaring your own `kind: "version"` selector here replaces the automatic one, so hand-rolled setups keep working.

## Featured links

Pin links to the top of the sidebar, above every section — a blog, a changelog, a contact or support page that should always be one click away. Unlike the generated tree, featured links are **not scoped by tab**: they show on every route, on every breakpoint.

```ts blume.config.ts lineNumbers
navigation: {
  featured: [
    { label: "Blog", href: "https://example.com/blog", icon: "newspaper" },
    { label: "Contact", href: "/contact", icon: "headphones" },
  ],
}
```

Each link takes a `label`, an `href`, and an optional `icon` (a [built-in icon](/docs/content/components#icon) name, image path/URL, or inline SVG — the same as anywhere else). An `href` may point anywhere: an external URL opens in a new tab, while an internal route (`/contact`) is validated against your pages at build time, warning you if nothing matches.

## Explicit sidebar

For full control, list explicit items in `navigation.sidebar` — a bare array is shorthand for `sidebar.items`, and the object form combines them with a global [`display`](#display-modes). When items are set, Blume uses them verbatim and skips file-system generation:

```ts blume.config.ts lineNumbers
navigation: {
  sidebar: [
    "/", // a page, referenced by route
    {
      label: "Guides", // a group
      collapsed: false,
      items: ["/configuration", "/configuration/theming"],
    },
    { label: "GitHub", href: "https://github.com/owner/repo" }, // an external link
  ],
}
```

Each item is a page route (a string), a group (`label` + `items`), or a link (`label` + `href`). Groups can nest, override the global [`display` mode](#display-modes), and start `collapsed`.

## Repository link

When you set [`github`](/docs/configuration) in your config, Blume shows a GitHub icon in the header — beside the theme toggle — that links to your repository. It's on by default; hide it with `navigation.repo`:

```ts blume.config.ts lineNumbers
navigation: {
  repo: false, // hide the header GitHub link (default: true)
}
```

The link only appears when `github` is configured, so projects without a repo are unaffected either way.

## Breadcrumbs and pagination

These come for free from the sidebar tree — no configuration:

- **Breadcrumbs** show the current page's parent group above the title.
- **Previous and next** links at the foot of each page follow sidebar order, skipping hidden pages.

## On this page

A right-rail outline is generated automatically from each page's `##` and `###` headings, so long pages stay scannable. On narrower screens, where the right rail is hidden, it collapses into an “On this page” dropdown above the content.

## Page actions

Below the table of contents, every page shows a set of quick actions:

- **Edit on GitHub** — links straight to the source file. Appears once you set [`github`](/docs/configuration) in your config.
- **Scroll to top** — smoothly returns to the top of long pages.
- **Give feedback** — opens a prefilled GitHub issue with an optional reaction and note (also requires `github`).

Others hand the page to AI tools — **Copy as Markdown** and **Open in chat** — covered in [AI](/docs/configuration/ai#copy-as-markdown).

With [`export`](/docs/configuration/export) on, an **Export** action also lets readers download the page as a PDF or EPUB.
