> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mercurjs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# How to Customize Navigation

> Reorder, hide, relabel, and re-parent the panel's built-in sidebar items from a single _navigation.ts file.

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.

<Info>
  **When to use this vs. a `config` export.** New pages you add via [drop-in routes](/rc/resources/tutorials/custom-panel-page) 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.
</Info>

## 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.

```typescript apps/vendor/src/extension-targets.d.ts theme={null}
/// <reference types="@mercurjs/vendor/extension-targets" />
```

With it present, `id` and `nested` autocomplete and an unknown id fails `tsc`.

## Author the navigation file

<Steps>
  <Step title="Create src/_navigation.ts">
    The file is host-owned and underscore-prefixed. Default-export a `defineNavigationConfig` with an `items` array of overrides.

    ```ts apps/vendor/src/_navigation.ts theme={null}
    import { defineNavigationConfig } from "@mercurjs/dashboard-sdk"

    export default defineNavigationConfig({
      items: [
        { id: "orders", rank: 0 },              // pin to the top
        { id: "price-lists", hidden: true },    // hide from the sidebar
        { id: "campaigns", nested: "orders" },  // re-parent under Orders
      ],
    })
    ```
  </Step>

  <Step title="Know the override fields">
    Each entry targets one built-in item by its stable `id`.

    | Field    | Type                  | Effect                                                                              |
    | -------- | --------------------- | ----------------------------------------------------------------------------------- |
    | `id`     | `NavItemId`           | **Required.** The built-in item to override, top-level or nested.                   |
    | `rank`   | `number`              | Order within its parent, lower first.                                               |
    | `hidden` | `boolean`             | Remove it from the sidebar.                                                         |
    | `label`  | `string`              | Relabel with an i18n key or literal.                                                |
    | `icon`   | `ComponentType`       | Replace its icon.                                                                   |
    | `nested` | `NavParentId \| null` | Re-parent under another top-level item. `null` promotes a nested item to top level. |

    Both `id` and `nested` are checked against the panel's generated `NavItemRegistry` and `NavParentRegistry`.
  </Step>

  <Step title="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.

    <Note>
      The route for a hidden item stays reachable directly by URL unless you also remove it.
    </Note>
  </Step>
</Steps>

## Common recipes

```ts theme={null}
export default defineNavigationConfig({
  items: [
    { id: "payouts", label: "Settlements" },        // relabel
    { id: "categories", nested: null, rank: 1 },    // promote a nested item to top level
    { id: "collections", nested: "orders" },        // move a nested item under a different parent
    { id: "inventory", hidden: true },              // hide a built-in
  ],
})
```

## 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

<AccordionGroup>
  <Accordion title="Can an installed block reorder the sidebar?">
    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`.
  </Accordion>

  <Accordion title="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`.
  </Accordion>

  <Accordion title="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.
  </Accordion>

  <Accordion title="How do I add a brand-new sidebar item?">
    That's a [drop-in route](/rc/resources/tutorials/custom-panel-page) with a `config` export. `_navigation.ts` only reshapes built-in items, it doesn't create routes.
  </Accordion>
</AccordionGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Add a custom panel page" href="/rc/resources/tutorials/custom-panel-page">
    Add a new screen with its own sidebar item.
  </Card>

  <Card title="Add a widget" href="/rc/resources/tutorials/add-a-widget">
    Inject a component into a built-in page.
  </Card>
</CardGroup>
