Skip to main content
Reshape the built-in sidebar without replacing it. You author one host-owned file, src/_navigation.ts, and it reorders, hides, relabels, and re-parents the built-in items. The sidebar ships a fixed set of items such as Orders, Products, and Customers. _navigation.ts is the single source of truth for their shape. It overrides existing items only, so a new item still comes from a page you add.
When to use this vs. a config export. New pages you add via drop-in routes place their own sidebar item through defineRouteConfig({ label, rank, nested }). _navigation.ts is for the items you didn’t create, the built-in ones. The two layer cleanly: custom routes place themselves, and _navigation.ts reshapes the built-ins.

What you’ll build

A vendor sidebar with Orders pinned to the top, Price Lists hidden, and Campaigns moved under Orders.

Register the typed targets

Nav item ids are typed and generated per panel. Register them once with a single ambient reference in your app’s src. The create-mercur-app scaffold already ships this file.
apps/vendor/src/extension-targets.d.ts
With it present, id and nested autocomplete and an unknown id fails tsc.

Author the navigation file

1

Create src/_navigation.ts

The file is host-owned and underscore-prefixed. Default-export a defineNavigationConfig with an items array of overrides.
apps/vendor/src/_navigation.ts
2

Know the override fields

Each entry targets one built-in item by its stable id.id, nested, and group are checked against the panel’s generated NavItemRegistry, NavParentRegistry, and NavGroupRegistry.
3

Reload the panel

Open the vendor portal. Orders sits at the top, Price Lists is gone from the menu, and Campaigns now appears under Orders.
The route for a hidden item stays reachable directly by URL unless you also remove it.

Common recipes

Group items under headings

Declare groups by keyword in groups, then assign items to them with group. A group renders in whichever sidebar its items live in, and a group with no visible items isn’t rendered.

Main sidebar

The main sidebar ships without groups. Items you assign to a declared group move out of the default list and render below it, under the group’s heading.
apps/vendor/src/_navigation.ts
Only top-level items can be grouped. Nested children follow their parent.

Settings sidebar

The settings sidebar already lists its items under groups. The admin panel ships general, developer, and myAccount. The vendor portal ships general. You can reshape these built-in groups, add your own, and move items between them. Settings items are targeted by their path without the leading slash, such as settings/users.
apps/admin/src/_navigation.ts

Group fields

An item’s group only accepts a built-in group id or an id declared in the same file’s groups, so a typo fails tsc.

Group a custom page

A custom page picks its group in its own route config with the same keyword.
apps/admin/src/routes/settings/webhooks/page.tsx
A page whose group isn’t declared falls back to the default placement: the ungrouped list in the main sidebar, or the first group in the settings sidebar.

Verify

  1. The top-level order reflects your rank values, with orders first.
  2. price-lists no longer appears in the sidebar.
  3. campaigns renders as a child under Orders.
  4. Set id: "not-an-item". bun run lint (tsc) fails against NavItemRegistry.
  5. Delete _navigation.ts. The default sidebar returns.

FAQ

No. Navigation is deliberately host-only. Blocks can ship pages, widgets, and custom fields, but the sidebar order stays a single source of truth in your app’s _navigation.ts.
Yes. A block’s page sets group in its route config like any other page. If the host hasn’t declared that group, the page falls back to the default placement.
Any built-in item, top-level or nested, by its own id, such as orders, products, categories, collections, campaigns, or customer-groups. Let your editor autocomplete id: against NavItemId. The full set is generated into your panel’s extension-targets.d.ts.
Yes. Drop src/_navigation.ts in the admin app and reference @mercurjs/admin/extension-targets. Each panel ships its own nav id set.
That’s a drop-in route with a config export. _navigation.ts only reshapes built-in items, it doesn’t create routes.

Next steps

Add a custom panel page

Add a new screen with its own sidebar item.

Add a widget

Inject a component into a built-in page.