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/astro";

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 composeEntries(collection).entries. 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 — composeEntries 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 getNavigation, 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.
  • Reach for getNavigation in a site that renders many pages: it takes the same arguments and answers with the tree already built for that entries array, keyed on the array itself. createNavigation rebuilds on every call, which is what you want for a one-off, a test, or any options carrying a transform — no key describes a function, so those are uncacheable and build every time.
  • Map entries with composeEntries, or toContentEntry one at a time — 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.