Navigation
Data-driven nav variants and pathRules routing, plus the filesystem escape hatch for exotic layouts.
Publier’s top navigation has two layers:
- Data —
publier.config.yaml#navdescribes content (title, logo, links, CTA) per variant plus URL routing rules. - Layout — Publier renders the variant you picked. Drop your own
src/components/TopNav.astroto replace it entirely.
The data layer covers the 90% case. The filesystem escape hatch covers the other 10%.
Flat form — one nav, whole site
Start here if you only need one nav across every page:
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-startedPublier coerces this into a single default variant. Every page renders the same nav.
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:
---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.”