Nimbus builds a typed sidebar tree from your content. Start with the filesystem, take control with configuration, or transform the final tree for navigation that depends on application data or the current route.
Order and label pages
The sidebar is generated from src/content/docs/ by default. Set sidebar.items in astro.config.ts only when you need to override that structure. Use sidebar.order in a page’s frontmatter, such as src/content/docs/guides/install.mdx, to control ordering; otherwise entries are alphabetical.
---
title: Install the CLI
sidebar:
order: 1
label: Install
badge:
text: New
variant: tip
---Frontmatter can also hide entries, customize directory groups, redirect links, or collapse a section. See Hide or change one page’s entry.
Arrange the sidebar by hand
In astro.config.ts, use sidebar.items when the filesystem should not define the entire rail. Config items can be nested and can mix manual links with generated content:
sidebar: {
items: [
"introduction",
{ label: "GitHub", link: "https://github.com/you/repo" },
{
label: "Guides",
autogenerate: { directory: "guides" },
collapsed: false,
badge: "New",
icon: "ph:book-open",
},
{
label: "API",
autogenerate: { collection: "api" },
icon: "ph:code",
},
{
label: "AI",
segment: "/ai",
landing: "/ai/models",
items: [
{ label: "Models", link: "/ai/models" },
{ autogenerate: { directory: "ai/guides" } },
],
},
],
}| Shape | Use |
|---|---|
"slug" |
Link one page from the primary collection. |
{ label, link } |
Add a labeled internal or external link. |
{ autogenerate: { directory } } |
Generate a group from one content directory. |
{ autogenerate: { collection, prefix? } } |
Mount another registered collection, linked where it’s served (Collection URLs). prefix sets the URL for a collection served by your own route, not a Nimbus route helper; for one that is, the route sets the URL and prefix is deprecated. |
{ label, items } |
Build a recursive manual group. Groups support collapsed, badge, and icon. |
{ segment, landing } |
Give a manual group ownership of a URL segment while linking its label and breadcrumbs to a real landing page. |
Array position controls manual item order. Nimbus does not re-sort a configured or transformed array.
Show a collection inside a folder
A collection served inside a docs folder’s URL appears in that folder. Take src/content/docs/integrations/connectors/ and a dbt collection routed at src/pages/integrations/connectors/dbt/[...slug].astro (see Collection URLs). dbt becomes a group inside Connectors, with /integrations/connectors/dbt/ as its landing.
Breadcrumbs and previous and next links follow, because they read the sidebar. A page the sidebar leaves out gets the breadcrumbs of the deepest group above its URL.
This works wherever the folder is generated, by default or with autogenerate, but never among a hand-made group’s own items.
If folders in between are missing, the collection goes in the deepest one that exists. A folder whose only page is its index becomes a group to hold it, keeping that page’s label.
The collection’s own index page controls the group, as a folder’s does. Set its label and position:
---
title: dbt
sidebar:
order: 2 # its position among the folder's pages; pages with an order come first,
# and a group without one takes its lowest child's
group:
label: dbt Core # the group label; the title otherwise
---With no index page, the group is a heading named after the URL’s last segment (Dbt). Show only its landing link with sidebar.hideChildren: true, or leave the whole collection out of the sidebar, along with any collection served inside it, with sidebar.hidden: true.
To place it yourself, name it in sidebar.items; it then appears only there:
{ autogenerate: { collection: "dbt" }, label: "dbt" }shows its pages as a group with its landing. Withoutlabel, its pages go straight into the surrounding group, and its landing isn’t linked.{ label: "dbt", link: "/integrations/connectors/dbt/" }, or a hand-made group’slanding, shows only its landing page.
Not placed:
- a collection one segment deep (
/blog), whose parent is the site root; - an API collection, which has its own navigation;
- anything on another docs version’s pages;
- a collection’s alias routes (only the URL it’s served at counts).
In astro dev, edits to a placed collection’s pages under src/content show on reload. A collection loaded from elsewhere, or from a content source, shows changes to its navigation after a restart.
Show only the current section
A site with many sections overflows a rail that lists everything. Set scope: "section" and each page’s rail shows only its own top-level group:
sidebar: {
scope: "section",
}A section is a top-level group that lists another collection, written autogenerate: { collection: "api" }. Groups of the main docs collection’s own pages, by directory or items, aren’t sections.
With two or more sections, the starter’s header shows them as tabs: in the bar on wide screens, and on a row of their own on phones. It builds them from the same tree with getSidebarSections(), and a header of your own can too:
---
import { getSidebarSections } from "@cloudflare/nimbus-docs";
const sections = await getSidebarSections(currentSlug, {
collection: entry.collection,
});
---scope: "full", the default, renders the complete tree on every page.
Give a deep folder its own sidebar
When one section is itself large, such as a reference with a folder per product, scope the rail one level deeper. List the folders that should get their own rail as segment globs, where * matches one path segment:
sidebar: {
scope: "section",
isolate: { boundaries: ["reference/*"] },
}A page under /reference/cli/ now shows only the reference/cli group. Pages outside a boundary keep the section rail. The rail starts with the group’s landing page, labeled like an overview leaf under indexDisplay: "overview-leaf".
Collapse groups until they’re needed
A long rail is easier to scan when its groups start closed. Set defaultCollapsed: true:
sidebar: {
defaultCollapsed: true,
}The group holding the current page still opens. A group with its own collapsed: false in sidebar.items stays open, so a “Start here” group can stay visible while the rest close.
Choose how group landing pages appear
A directory index.mdx is its group’s landing page. Configure how those landings appear across the site in astro.config.ts:
sidebar: {
indexDisplay: "overview-leaf",
overviewLabel: "Overview",
}| Option | Behavior |
|---|---|
indexDisplay: "header-link" |
Default. The group heading links to its landing page. |
indexDisplay: "overview-leaf" |
The group heading becomes a disclosure and the landing becomes its first child. |
overviewLabel: true |
Labels landing links Overview. A string supplies a custom label. |
sidebar.group.hideIndex: true |
Keeps the page built but makes its group heading non-interactive. |
sidebar.hideChildren: true |
Collapses the whole directory to one link to its landing page. |
Change the sidebar per page
Configuration defines the structural tree. getSidebar() also accepts a synchronous or asynchronous transform for computed navigation. It runs after scope and isolation, but before indexDisplay reshapes landing pages.
---
import { getPrevNext, getSidebar } from "@cloudflare/nimbus-docs";
const sidebar = await getSidebar(currentSlug, {
collection: entry.collection,
transform: ({ tree, sectionSlug }) => {
if (sectionSlug !== "api") return tree;
return tree.map((item) =>
item.type === "group" && item.label === "Reference"
? {
...item,
collapsed: false,
badge: "API",
children: [
...item.children,
{
type: "external",
label: "API status",
href: "https://status.example.com",
order: Number.MAX_SAFE_INTEGER,
},
],
}
: item,
);
},
});
const prevNext = await getPrevNext(currentSlug, {
sidebarTree: sidebar,
});
---The callback receives:
| Value | Meaning |
|---|---|
tree |
The cloned, active-state-aware SidebarItem[] for this page. Nodes are link, external, or recursive group items. |
currentSlug |
The complete current URL path. |
sectionSlug |
The first path segment. |
module |
The second path segment, when present. |
indexEntryId |
The active section group’s landing entry ID, when it has one. |
A transform can fetch data, insert or remove nodes, rewrite nested groups, attach badges, or reorder the rail. Return rendered SidebarItem nodes, not config-only shapes such as autogenerate. Preserve existing properties when decorating nodes, and pass the result to getPrevNext() when pagination should follow the transformed order.
Transforms only affect the page rail. Breadcrumbs and getSidebarSections() continue to use the structural tree.
Hide or change one page’s entry
Use frontmatter for local changes:
| Field | Behavior |
|---|---|
sidebar: false |
Removes the sidebar column from this page. |
sidebar.order |
Orders an entry inside an autogenerated group. |
sidebar.label |
Overrides the page title in the rail. |
sidebar.badge |
Adds a string or styled badge. |
sidebar.hidden |
Builds the page but removes it from the rail. |
sidebar.group.label |
Overrides the directory group’s label from its index.mdx. |
sidebar.group.badge |
Adds a badge to the directory group. |
sidebar.group.icon |
Adds an astro-icon icon to the directory group. |
sidebar.group.hideIndex |
Prevents the directory group heading from linking to its index. |
sidebar.hideChildren or hideChildren |
Collapses the directory to its landing link. |
external_link |
Points the entry at another internal path or an absolute URL. The page still builds at its own path, and absolute URLs open in a new tab. |
Set features.sidebar: false in astro.config.ts to disable the rail site-wide. See Frontmatter for complete field shapes and badge variants.
Restyle sidebar rows
Sidebar groups are native <details>/<summary> elements: opening and closing needs no JavaScript, links in closed groups stay in the HTML for find-in-page and crawlers, and closed lists carry display: none so large trees stay fast. A group with a landing page lists it as the group’s first child, labelled “Overview” — the summary row is label-only.
Rows carry no class or data attribute, so a sidebar of thousands of pages stays light. The “Sidebar rows” section of src/styles/globals.css styles them by position inside the sidebar ([data-nb-sidebar], and [data-nb-api-nav] for the API reference). Edit those rules to restyle every row. They sit in Tailwind’s components layer, so a Tailwind utility on a row still overrides them:
| Selector | Element |
|---|---|
[data-nb-sidebar] li > a |
A leaf row: <a href>Label</a>. The current page carries aria-current="page". |
[data-nb-sidebar] details > summary |
A group’s row. The caret is its ::after mask, rotated by details[open]; the active trail carries data-nb-active. |
[data-nb-sidebar] details > ul |
A group’s child list. |
i[data-m] |
An API operation’s method chip; data-m is the lowercase method. |
Each page renders one sidebar tree: the mobile drawer borrows the rail’s tree while it is open and returns it on close, so open groups, the filter, and scroll position stay with you across the breakpoint.