Navigation & Menus

Configure the navbar, footer, and social links

Last updated October 21, 2026


Every menu shares one data model and one set of render components, so filtering rules, external-link behavior, and keyboard handling are identical across the navbar, footer, and docs sidebar.

FileResponsibility
src/config/navigation.tsMenu types, link targets, filtering rules
src/config/navbar-config.tsNavbar links and right-side actions
src/config/footer-config.tsFooter groups
src/config/social-config.tsSocial platform mapping and filtering

Prerequisites

Read Website Config first — menu entries show and hide based on module switches.

Setup

Edit src/config/navbar-config.ts:

ts
// src/config/navbar-config.ts
{
  kind: 'link',
  id: 'pricing',
  title: 'Pricing',
  destination: { kind: 'internal', options: getPublicLinkOptions('pricing', locale) },
  visibility: { module: 'payment' },   // hidden when payment is off
}

getPublicLinkOptions() returns type-safe linkOptions(). Internal links are validated by TanStack Router — a missing route, missing dynamic params, or an internal link written as external is rejected at `tsc` time.

targetResolves to
home/{locale}
docs/{locale}/docs
blog/{locale}/marketing/blog
changelog/{locale}/marketing/changelog
pricing/{locale}/pricing
contact/{locale}/marketing/contact
roadmap/{locale}/marketing/roadmap
waitlist/{locale}/marketing/waitlist
login/{locale}/auth/login
terms / privacy/{locale}/marketing/legal/{slug}

Visibility filtering

All four conditions in visibility must hold for an entry to render:

FieldMeaning
moduleModule switch, e.g. { module: 'payment' }
featureFeature switch, e.g. { feature: 'enableApiKeys' }
authenticatedWhether sign-in is required
rolesRole allowlist
Entries pointing at contact must carry visibility: { module: 'contact' } — that page is off by default and the link would 404. roadmap / waitlist are static pages with no module switch and need no visibility.

Entry shapes

kindNotes
linkInternal (internal + options) or external (external + href + newTab)
groupRecursive children; the group disappears when every child is filtered out
separatorRemoved automatically at the start, end, or when duplicated

All three carry id, optional title / description / icon, and visibility.

The footer lives in src/config/footer-config.ts and uses the same structure. Social links go in social on src/config/website.ts:

ts
social: {
  github: 'https://github.com/your-org/your-repo',
  x: 'https://x.com/your-handle',
}

Only valid HTTPS URLs without credentials render; anything else is dropped silently.

References

What's next