Skip to content

createNavigation

Builds the sidebar tree once and binds every navigation lookup to it.

View as Markdown

Creates a Navigation over a list of content entries. It builds the sidebar tree a single time and binds the four lookups a page needs to that one tree, so the rail, the breadcrumbs, and the pager can never disagree about what the site contains.

ts
import { createNavigation } from "@teasim/astra";

const docs = createNavigation(entries, { base: "/docs" });

docs.sidebar;                              // the tree, as getSidebar builds it
docs.prevNext("guides/installing");        // { prev, next }
docs.breadcrumbs("guides/installing");     // [{ label, href, current }, …]
docs.sections("guides/installing");        // top level, with the current one marked
docs.find("guides/installing");            // one node, or null

// prevNext reads the entry's own frontmatter, so a page hand-wires nothing.
docs.prevNext("intro");                    // honours `next: false` in intro's frontmatter

// An explicit override still wins.
docs.prevNext("intro", { next: "guides/installing" });

// Nothing here reads astro:content, touches the filesystem, or needs a build.
createNavigation([{ slug: "a", title: "Alpha" }]).sidebar[0].href; // "/a"

API Reference

Signature

ts
createNavigation(entries: readonly ContentEntry[], options?: SidebarOptions): Navigation;

Parameters

Parameter Type Required Notes
entries readonly ContentEntry[] Yes Usually collection.map(toContentEntry). Order is not significant — the tree sorts by sidebar.order, then label
options SidebarOptions No base, trailingSlash, includeDrafts, transform

SidebarOptions

Option Type Default Notes
base string "/" Prefix for every href; a path or an absolute URL. A path with no leading slash is still rooted
trailingSlash boolean false Append / to every href except the root
includeDrafts boolean false Keep entries marked draft
transform (nodes: SidebarNode[]) => SidebarNode[] Reshape the finished tree

Returns

Returns a Navigation — the tree plus four lookups bound to it.

Member Type Notes
sidebar SidebarNode[] The finished tree, already sorted and pruned
find(slug) SidebarNode | null null for a heading, an unknown slug, or an empty one
breadcrumbs(slug) Breadcrumb[] Ancestry, current: true on the last crumb; [] for an unknown slug
prevNext(slug, overrides?) PrevNext Both ends SidebarLink | null; never wraps
sections(slug) SidebarSection[] The top level, with the one holding slug marked current

Throws

Nothing. Bad content does the most sensible thing and carries on — two entries that normalise to one slug become one node, last wins. That is how a duplicate slug becomes one silently missing page, so run validateContent on the same entries when you want the problems named instead — the scaffolded src/helpers/docs.ts does exactly that, in dev only.

Agent Contract

Field Value
Kind factory
Canonical name createNavigation
Aliases None
Mutates input No
Returns Navigation
Reads astro:content No
Needs a build No
Configuration dependencies None
Related getSidebar, getPrevNext, getBreadcrumbs, getSidebarSections, findSidebarNode

Agent Notes

  • Generate createNavigation(entries, options) for a page. The five underlying functions stay exported for a test driving one answer, or a page that wants one without the rest.
  • Do not thread { prev: entry.data.prev, next: entry.data.next } into prevNext — it reads the entry’s frontmatter itself. A mapper that forgets makes those two schema fields silently inert.
  • Build it once per page, not once per lookup. Each call rebuilds the tree.
  • Map entries with toContentEntry, not by hand: a hand mapper that drops externalLink, prev, next, or previousSlug makes those fields inert and validateContent cannot report what it never receives.
  • Both ends of prevNext are SidebarLink, whose slug and href are never null. Do not generate a null check for them.
  • breadcrumbs keeps headings and prevNext skips them, so a trail is not a slice of the reading order. Do not derive one from the other.
  • Pass base: import.meta.env.BASE_URL in an Astro project so every href survives a deploy under a subpath.