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’ssrc. The create-mercur-app scaffold already ships this file.
apps/vendor/src/extension-targets.d.ts
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 ingroups, 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
Settings sidebar
The settings sidebar already lists its items under groups. The admin panel shipsgeneral, 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
- The top-level order reflects your
rankvalues, withordersfirst. price-listsno longer appears in the sidebar.campaignsrenders as a child under Orders.- Set
id: "not-an-item".bun run lint(tsc) fails againstNavItemRegistry. - Delete
_navigation.ts. The default sidebar returns.
FAQ
Can a block put its page in a group?
Can a block put its page in a group?
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.What ids can I target?
What ids can I target?
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.Does this work in the admin panel too?
Does this work in the admin panel too?
Yes. Drop
src/_navigation.ts in the admin app and reference @mercurjs/admin/extension-targets. Each panel ships its own nav id set.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.