Skip to main content
Both the admin panel and vendor portal use the same SDK (@mercurjs/dashboard-sdk). Customization is file-based and convention-driven — you add pages by creating files, configure navigation through exports, and override components through config.

Setup

Every panel project needs two files at its root:

Vite config

vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { dashboardPlugin } from '@mercurjs/dashboard-sdk'

export default defineConfig({
  plugins: [react(), dashboardPlugin()],
})

Mercur config

mercur.config.ts
import { defineConfig } from '@mercurjs/dashboard-sdk'

export default defineConfig({
  name: 'My Marketplace',
  logo: 'https://example.com/logo.svg',
})
Available options:
OptionTypeDescription
namestringApplication name shown in the sidebar
logostringURL to a logo image
componentsobjectComponent overrides (see Replacing components)
i18nobjectInternationalization settings
backendUrlstringMedusa backend URL (default: http://localhost:9000)

Adding pages

Create a page.tsx file inside src/routes/ and export a default React component. The route is determined by the file path.
src/routes/reviews/page.tsx
import { Star } from "@medusajs/icons"
import type { RouteConfig } from "@mercurjs/dashboard-sdk"

export const config: RouteConfig = {
  label: "Reviews",
  icon: Star,
  rank: 10,
}

export default function ReviewsPage() {
  return <div>Reviews</div>
}
This creates a /reviews route and adds a “Reviews” item to the sidebar.

The config export

The config export controls how the page appears in navigation:
PropertyTypeDescription
labelstringRequired. Text shown in the sidebar menu
iconComponentTypeIcon component (e.g. from @medusajs/icons)
ranknumberSort order — lower numbers appear first
nestedstringParent path for nested menu items
translationNsstringi18n namespace for the label
publicbooleanIf true, the route is accessible without authentication
Pages without a config export are still routed but won’t appear in the sidebar.

Routing conventions

File paths map to URL routes automatically:
File pathRouteDescription
src/routes/page.tsx/Root page
src/routes/reviews/page.tsx/reviewsStatic segment
src/routes/reviews/[id]/page.tsx/reviews/:idDynamic segment
src/routes/reviews/[[id]]/page.tsx/reviews/:id?Optional dynamic segment
src/routes/search/[*].tsx/search/*Catch-all
src/routes/(settings)/page.tsxRoute groupingGroups routes without adding a URL segment
src/routes/dashboard/@sidebar/page.tsxParallel routeRenders alongside parent
Sidebar items are generated from pages that export a config with a label.

Ordering

Use rank to control the order. Lower values appear higher:
// src/routes/orders/page.tsx
export const config: RouteConfig = {
  label: "Orders",
  icon: ShoppingCart,
  rank: 1,
}

// src/routes/products/page.tsx
export const config: RouteConfig = {
  label: "Products",
  icon: Tag,
  rank: 2,
}

Nested menus

Use nested to group pages under a parent:
src/routes/settings/shipping/page.tsx
export const config: RouteConfig = {
  label: "Shipping",
  nested: "/settings",
}
This places “Shipping” as a child item under /settings in the sidebar.

Branding

Set name and logo in mercur.config.ts to customize the sidebar header:
mercur.config.ts
import { defineConfig } from '@mercurjs/dashboard-sdk'

export default defineConfig({
  name: 'WeTest',
  logo: 'https://ui-avatars.com/api/?name=WeTest&background=18181B&color=fff&size=200&bold=true&format=svg',
})

Replacing components

Override built-in layout components in mercur.config.ts:
mercur.config.ts
import { defineConfig } from '@mercurjs/dashboard-sdk'

export default defineConfig({
  name: 'My Marketplace',
  components: {
    MainSidebar: 'components/custom-sidebar',
    SettingsSidebar: 'components/custom-settings-sidebar',
    TopbarActions: 'components/custom-topbar-actions',
  },
})
Paths are relative to src/. Each file must have a default export:
src/components/custom-topbar-actions.tsx
export default function CustomTopbarActions() {
  return (
    <div>
      <button>Help</button>
      <button>Notifications</button>
    </div>
  )
}
ComponentDescription
MainSidebarPrimary navigation sidebar
SettingsSidebarSettings section sidebar
TopbarActionsAction buttons in the top bar

Internationalization

1. Create translation resources
src/i18n/index.ts
export default {
  en: {
    reviews: {
      title: "Reviews",
      description: "Manage product reviews",
    },
  },
  de: {
    reviews: {
      title: "Bewertungen",
      description: "Produktbewertungen verwalten",
    },
  },
}
2. Reference the namespace in your page config
src/routes/reviews/page.tsx
export const config: RouteConfig = {
  label: "reviews.title",
  icon: Star,
  translationNs: "reviews",
}
3. Set the default language
mercur.config.ts
import { defineConfig } from '@mercurjs/dashboard-sdk'

export default defineConfig({
  name: 'My Marketplace',
  i18n: {
    defaultLanguage: 'en',
  },
})