Skip to main content
A page is a React component mounted at a route. Pages are file-based. You drop a page.tsx under a panel’s src/routes/ folder and the SDK registers it at build time, the same way Next.js and Remix map folders to routes. This guide walks through creating a page, then documents the routing conventions, the sidebar config, and the defineNavigationConfig and createFormHelper helpers.

Create a page

1

Add a page file

Create page.tsx in a new folder under src/routes/. The folder path becomes the URL, so this file mounts at /erp-sync.
apps/vendor/src/routes/erp-sync/page.tsx
The default export is the only required part. Only files named page register as routes, so you can co-locate a loader.ts or components in the same folder.
2

Add it to the sidebar

A page has no sidebar entry until it exports a config with a label. The config is a plain object.
apps/vendor/src/routes/erp-sync/page.tsx
3

Run the panel

Start the panel and open the route. The page renders and its sidebar entry appears.
Terminal
The vendor panel runs on http://localhost:7001 and the admin panel on http://localhost:7000.

Route paths

Folder and file names map to path segments. Wrap a segment in brackets or parentheses to make it dynamic or optional. Read a dynamic segment with React Router’s useParams(). A file at src/routes/orders/[id]/page.tsx mounts at /orders/:id.

Where the page mounts

The route path and the page’s config.public flag decide which layout wraps the page. Main and settings routes render inside the authenticated shell. Public routes render on their own, so use them for pages a signed-out user must reach. The config object controls the page’s sidebar entry.
A page can exist without a sidebar entry. Omit label when the route is reached from a link or a widget rather than the sidebar.

Load data for a page

Export a loader to fetch data before the component renders, and a handle to attach route metadata such as a breadcrumb. Both are React Router route options, picked up as named exports.
apps/vendor/src/routes/erp-sync/page.tsx
Read the loader’s result with useLoaderData() inside the component.

Reshape built-in navigation

Use defineNavigationConfig to reorder, hide, relabel, or re-parent built-in sidebar items. It lives in a single host-owned file, src/_navigation.ts. Blocks cannot contribute navigation overrides.
apps/vendor/src/_navigation.ts
Each entry is a NavItemOverride. Navigation overrides reshape existing items only. To add a new item, register a page with a config.label as shown above.

Type a page’s forms

createFormHelper<T>() returns Medusa’s Zod surface for describing field validation and value types. Import it from @mercurjs/dashboard-shared.
The generated registry types the target: which zone, tab, and field ids exist. The Zod validation types the value. See Custom Fields for where form.define fields are attached.

Next steps

Widgets

Render a component in a slot on an existing page.

Custom Fields

Add fields, section rows, and list columns to a built-in model.