Skip to content
✨ Publier v1 is live — a polished docs platform built for the open web.
On this page

Navigation

Configure navigation links, dropdown groups, product and version selectors, and per-path variants for marketing and documentation pages.

Publier’s top navigation has two layers:

  1. Data — publier.config.yaml#nav describes content (title, logo, links, CTA) per variant plus URL routing rules.
  2. Layout — Publier renders the variant you picked. Drop your own src/components/TopNav.astro to replace it entirely.

The data layer covers the 90% case. The filesystem escape hatch covers the other 10%.

Flat form — default navigation

Start here for pages that share default navigation:

publier.config.yaml
nav:
title: My Site
logo: /logo.svg
links:
- { label: Docs, href: /docs }
- { label: GitHub, href: https://github.com/acme/product, external: true }
cta:
label: Get started
href: /docs/getting-started

Publier coerces this into a single default variant. Pages using default navigation share these links. The built-in documentation route uses a separate docs variant, or derives documentation section links when that variant is absent.

Use a group when readers need to choose among related destinations, such as products, solutions, or learning resources. Both tabs and links accept groups:

nav:
title: Acme
tabs:
- label: Products
items:
- label: All products
href: /products
description: Find the product that fits your team.
- label: Workspace
href: /products/workspace
description: Collaborate on shared projects.
- label: Resources
items:
- label: Documentation
href: /docs
description: Concepts and practical guides.
- label: Community
href: https://example.com/community
external: true
links:
- label: Sign in
href: /login
cta:
label: Contact us
href: /contact

A group has a label and an items list. Each item needs label and href; description and external are optional. Groups contain one level of destination links. Include an overview link as an item when the group needs a landing page; the group’s trigger opens the menu rather than navigating.

Desktop marketing groups open on hover with a fine pointer, or with click and keyboard activation. Panels span the header width, arrange links in two columns, and use a short Motion entrance; reduced-motion preferences disable it. Panel and link corners follow the theme’s --radius-box and --radius-selector tokens. The inner content aligns with --publier-page-max-width. Hover keeps the reader’s current focus, and moving into the panel keeps it open. Escape and outside clicks dismiss the menu. Touch uses tap. Below the large breakpoint, navigation opens in a dialog with expandable groups. Existing flat links remain supported, so you can add a group without converting every link.

Keep marketing product and solution destinations separate from documentation. Short descriptions help readers understand which destination matches their intent before they choose it.

Documentation products and versions

Use products on a navigation variant when readers need to switch between documentation products. Each product has its own optional versions list. This is separate from marketing dropdown groups: the trigger reflects the product and version for the current URL.

nav:
default: marketing
pathRules:
- { path: /docs/**, variant: docs }
- { path: /docs, variant: docs }
variants:
marketing:
title: Acme
links: [{ label: Documentation, href: /docs }]
docs:
title: Acme
products:
- { label: Overview, path: /docs, tabs: [] }
- label: Compute
path: /docs/compute
href: /docs/compute/v2
versions:
- { label: v2, path: /docs/compute/v2, badge: stable, default: true }
- { label: v1, path: /docs/compute/v1, badge: legacy }
- label: Enterprise
path: /docs/enterprise
tabs:
- { label: Documentation, href: /docs/enterprise }
- { label: Guides, href: /docs/enterprise/guides }
- { label: API reference, href: /docs/enterprise/api }
links: [{ label: Home, href: / }]

path is the product’s local URL prefix. href is its landing destination; it defaults to path. The longest matching product prefix wins, with whole path segments respected: /docs/compute does not match /docs/compute-enterprise.

For the selected product, the longest matching version path wins. When no version path matches, default: true selects the fallback; otherwise the first version is shown. A version’s path is its destination and must be within its product’s prefix. An optional badge adds a short label beside the version in the menu.

ConfigurationNavigation behavior
Multiple products with versionsProduct selector plus the selected product’s version selector
Product with no versions, or versions: []Product selection without a version selector
One product with versionsProduct link plus a version selector, with no product dropdown
No products, or products: []No product or version selectors, unless the default docs route has legacy docs.sidebar.versions

Selectors and section links share the same header row on desktop and appear inside the navigation dialog on small screens. Documentation selectors open on click or keyboard activation; their labels remain stationary while the arrow rotates smoothly. Reduced-motion preferences disable the arrow transition. They support menu navigation, Escape, and selection. Selection visits the configured destination; Publier does not create documentation versions, copy pages, or guess a corresponding page in another version. Publish each destination yourself before linking it.

Each product can supply tabs for its own documentation sections. Omit tabs to inherit the variant’s links; use tabs: [] to hide them. An overview product such as /docs can use tabs: [] while selected products show their own Documentation, Guides, and API reference links. When products are configured but no product prefix matches, section links stay hidden. These rules apply to desktop and mobile navigation.

The default docs route also scopes sidebars using each product tab’s local /docs/ destination. The longest matching whole-segment prefix owns an article, so an API sidebar contains API pages and a parent Documentation sidebar excludes the nested API and Guides sections. This behavior requires shell 4.3.2/native 6.0.1 or later. Use documentation sections for additional content-folder scopes. Custom routes remain responsible for the sidebar data they pass to their components.

Since shell 4.4.0/native 7.0.0, pages and section groups within the selected scope appear directly in the sidebar, with the enclosing product and tab folders removed. A visible landing page becomes a normal Overview link at the top level. It returns to that tab’s landing page and is marked as the current page when selected, on desktop and mobile. A scope without a visible landing page has no Overview link. Remaining groups keep their configured labels, order, and collapse settings; page destinations keep their full URLs. Sidebars without a selected scope retain their full hierarchy.

For a custom docs route, pass the selected content-folder path to buildSidebar from @publier/shell/search:

const sidebar = buildSidebar(sectionPages, {
scope: 'enterprise/api',
meta: sidebarMeta,
defaultOpenLevel: 1,
});

sectionPages must contain the pages belonging to that section, excluding separately scoped nested tabs. Keep their complete slugs and URLs, and keep metadata keyed by complete folder paths. Omitting scope preserves the full sidebar hierarchy.

You can use products in the flat nav form too. Use variants when only the documentation area should show selectors. Product paths and version paths must be unique in their respective lists, and a product may have only one default version. Invalid selector configuration fails the build with a field path.

See Versioning for content layout and the optional standalone version switcher.

Dedicated documentation header

The docs variant puts brand, product/version selection, section links, and utility controls in one header row. For a single-product site, configure sections through nav.variants.docs.tabs (or the compact nav.docs.tabs form):

nav:
variants:
docs:
title: Acme
tabs:
- { label: Documentation, href: /docs }
- { label: Guides, href: /docs/guides }
- { label: API reference, href: /docs/api }
- { label: Changelog, href: /changelog }
links: [{ label: Support, href: 'mailto:support@example.com' }]

Publish each destination before adding its tab. Tabs are links rather than an ARIA tab widget: selecting one opens a page, and the current destination is marked for assistive technology. On small screens, section links and product/version selection are available through the header’s navigation menu. Sites without selectors keep their section links in the same primary row.

The default docs route uses the dedicated docs variant and keeps its sidebar focused on page links. It does not repeat a section or product selector there. Without a configured docs variant, Publier derives section links from docs.tabSections and uses the site’s branding, instead of inheriting marketing links or calls to action. For documentation without selectors, omit products (or set products: []) and leave docs.sidebar.versions empty. The default docs route moves legacy docs.sidebar.versions into the header when no products are configured; it renders a Documentation link and a version selector. Explicit products take precedence over that legacy setting.

Variants + pathRules — per-path auto-routing

When your site has distinct zones (landing / docs / blog) with different navs, use named variants plus a URL routing table:

nav:
# Name of the fallback variant when no pathRule matches.
default: default
# First-match-wins routing. Evaluated against the current pathname
# when a page doesn't pass an explicit `nav` prop.
pathRules:
- { path: /docs/**, variant: docs }
- { path: /docs, variant: docs }
- { path: /blog/**, variant: blog }
- { path: /, variant: marketing }
- { path: /pricing, variant: marketing }
- { path: /login, variant: false } # disable nav entirely
- { path: /*, variant: default }
variants:
default:
title: Acme
logo: /logo.svg
links: [{ label: Docs, href: /docs }]
cta: { label: Get started, href: /signup }
docs:
title: Acme Docs
logo: /logo.svg
tabs:
- { label: Documentation, href: /docs }
- { label: Guides, href: /docs/guides }
cta: { label: Get started, href: /signup }
marketing:
title: Acme
logo: /logo.svg
links:
- { label: Features, href: /#features }
- { label: Pricing, href: /pricing }
cta: { label: Start free, href: /signup }
blog:
title: Acme Blog
links: [{ label: Docs, href: /docs }]

Path glob vocabulary

  • * — any single path segment ([^/]+)
  • ** — any depth (.*, matches nested segments)
  • literal text — verbatim; regex metacharacters escaped

Patterns are anchored on both sides, so /docs/* matches /docs/intro but not /docs/guides/diagrams — use /docs/** for subtree matching.

variant: false disables the nav

A rule with variant: false renders no <TopNav> for matching paths. This is the same effect as <BaseLayout nav={false}> in a page, but declarative.

Per-page override (legacy escape hatch)

Any page can still pass nav="variantName" or nav={false} to override the path-based routing:

---
import { BaseLayout } from '@publier/shell/layouts';
---
<BaseLayout title="Checkout" nav={false}>
<!-- no nav on this page regardless of pathRules -->
</BaseLayout>

Explicit props always win over pathRules.

Filesystem override — full control

YAML is data for the common cases. If you need layout logic the data model can’t express — custom zone layouts, per-variant CTA animations, conditional greetings, an A/B test — drop your own src/components/TopNav.astro file in your project:

src/components/TopNav.astro
---
interface Props {
variant: string;
currentPath: string;
}
const { variant, currentPath } = Astro.props;
---
<header class="navbar …">
<!-- whatever markup you need -->
</header>

Astro’s filesystem resolution picks your copy over the default. No configuration flag required — the file’s existence is the opt-in.

When NOT to reach for YAML

Don’t try to express positioning, animation, or conditional logic in publier.config.yaml. The schema intentionally stays lean — bloating it into a layout DSL would trade Publier’s zero-config DX for a brittle config surface. The filesystem override is the right lever for anything beyond “here’s my list of links and my CTA.”