@mercurjs/docs 2.2.0-rc.0 → 2.2.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/content/learn/concepts.mdx +1 -1
  2. package/content/references/overview.mdx +10 -0
  3. package/content/references/panel-extension-api.mdx +337 -0
  4. package/content/references/workflows/cart/add-seller-shipping-method-to-cart.mdx +5 -1
  5. package/content/references/workflows/commission/batch-commission-rules.mdx +6 -2
  6. package/content/references/workflows/offer/batch-offer-inventory-items.mdx +6 -2
  7. package/content/references/workflows/offer/create-offers.mdx +3 -1
  8. package/content/references/workflows/offer/update-offers.mdx +3 -1
  9. package/content/references/workflows/order-group/get-order-groups-list.mdx +3 -1
  10. package/content/references/workflows/payout/process-payout-for-webhook.mdx +3 -1
  11. package/content/references/workflows/product/confirm-products.mdx +2 -0
  12. package/content/references/workflows/product/reject-product.mdx +2 -0
  13. package/content/references/workflows/product/request-product-change.mdx +2 -0
  14. package/content/references/workflows/seller/approve-seller.mdx +2 -0
  15. package/content/references/workflows/seller/suspend-seller.mdx +2 -0
  16. package/content/references/workflows/seller/terminate-seller.mdx +2 -0
  17. package/content/references/workflows/seller/unsuspend-seller.mdx +2 -0
  18. package/content/references/workflows/seller/unterminate-seller.mdx +2 -0
  19. package/content/resources/best-practices/api-routes.mdx +306 -0
  20. package/content/resources/best-practices/custom-fields.mdx +241 -0
  21. package/content/resources/best-practices/frontend.mdx +286 -0
  22. package/content/resources/best-practices/module-links.mdx +157 -0
  23. package/content/resources/best-practices/modules.mdx +144 -0
  24. package/content/resources/best-practices/overview.mdx +115 -0
  25. package/content/resources/best-practices/subscribers-and-jobs.mdx +133 -0
  26. package/content/resources/best-practices/types.mdx +120 -0
  27. package/content/resources/best-practices/workflows.mdx +173 -0
  28. package/content/resources/customization/custom-fields.mdx +5 -1
  29. package/content/resources/customization/extending-panels.mdx +170 -113
  30. package/content/resources/tutorials/add-a-widget.mdx +110 -0
  31. package/content/resources/tutorials/add-order-detail-button.mdx +113 -0
  32. package/content/resources/tutorials/custom-panel-page.mdx +2 -2
  33. package/content/resources/tutorials/customize-navigation.mdx +110 -0
  34. package/content/resources/tutorials/extend-forms-and-tables.mdx +188 -0
  35. package/content/resources/tutorials/extend-onboarding.mdx +253 -0
  36. package/content/resources/tutorials/import-export-products.mdx +3 -3
  37. package/content/resources/tutorials/store-setup-checklist.mdx +214 -0
  38. package/llms.txt +17 -3
  39. package/package.json +1 -1
  40. package/content/resources/tutorials/recompose-a-page.mdx +0 -129
  41. package/content/resources/tutorials/replace-panel-components.mdx +0 -116
@@ -0,0 +1,214 @@
1
+ ---
2
+ title: "Build a store-setup checklist"
3
+ description: "Render an onboarding progress checklist on the vendor store-setup surface with a seller.setup widget — the seller is handed to you as data, no fetching required."
4
+ ---
5
+
6
+ The vendor **store-setup / onboarding** surface is a widget zone (`seller.setup`)
7
+ that renders the full `seller` object as its `data`. That makes it the perfect
8
+ seam for an onboarding **checklist**: derive completion from the seller you're
9
+ handed, show what's left, and link each step to the page that completes it — all
10
+ from a single widget file, with nothing fetched and nothing forked.
11
+
12
+ <Info>
13
+ **This is the pattern the built-in store-setup card uses.** The shipped
14
+ "Complete store profile" card is itself a `seller.setup` widget that reads the
15
+ `seller` and renders a progress list. This tutorial rebuilds it so you can
16
+ shape your own onboarding steps.
17
+ </Info>
18
+
19
+ ## What you'll build
20
+
21
+ A collapsible progress card on the store-setup surface that checks four parts of
22
+ the seller profile (details, address, company, payment), draws a progress bar,
23
+ and routes the vendor to the settings page for each incomplete step. When every
24
+ step is done, the card removes itself.
25
+
26
+ ## Register the typed targets (once)
27
+
28
+ Widget zones are typed ids the vendor panel generates from its own pages and
29
+ ships as `@mercurjs/vendor/extension-targets`. Reference them once so `seller.setup`
30
+ autocompletes and a wrong zone fails `tsc`:
31
+
32
+ ```typescript apps/vendor/src/extension-targets.d.ts
33
+ /// <reference types="@mercurjs/vendor/extension-targets" />
34
+ ```
35
+
36
+ ## Build the checklist widget
37
+
38
+ <Steps>
39
+ <Step title="Derive the steps from the seller">
40
+ The `data` prop is the full `SellerDTO`. Compute each step's `completed` flag
41
+ from it — no request needed. Give every step a `path` to the settings page
42
+ that finishes it:
43
+
44
+ ```tsx apps/vendor/src/widgets/store-setup-checklist.tsx
45
+ import "@mercurjs/vendor/extension-targets"
46
+ import { SellerDTO } from "@mercurjs/types"
47
+
48
+ type ProfileStep = {
49
+ key: string
50
+ label: string
51
+ completed: boolean
52
+ path: string
53
+ }
54
+
55
+ function getProfileSteps(seller: SellerDTO): ProfileStep[] {
56
+ const hasStoreDetails = !!(seller.name && seller.email && seller.description)
57
+ const hasAddress = !!(
58
+ seller.address?.address_1 &&
59
+ seller.address?.city &&
60
+ seller.address?.country_code
61
+ )
62
+ const hasCompanyDetails = !!seller.professional_details?.corporate_name
63
+ const hasPaymentDetails = !!(
64
+ seller.payment_details?.holder_name &&
65
+ seller.payment_details?.country_code
66
+ )
67
+
68
+ return [
69
+ { key: "store_details", label: "Add store details", completed: hasStoreDetails, path: "/settings/store/edit" },
70
+ { key: "address", label: "Add address", completed: hasAddress, path: "/settings/store/address" },
71
+ { key: "company_details", label: "Add company details", completed: hasCompanyDetails, path: "/settings/store/professional-details" },
72
+ { key: "payment_details", label: "Add payment details", completed: hasPaymentDetails, path: "/settings/store/payment-details" },
73
+ ]
74
+ }
75
+ ```
76
+
77
+ </Step>
78
+ <Step title="Render the collapsible progress card">
79
+ Use `@medusajs/ui` primitives and `@medusajs/icons` only. Draw a progress bar
80
+ from the completed ratio, list each step with a status icon, and navigate on
81
+ click. Return `null` when everything is done so the card disappears:
82
+
83
+ ```tsx apps/vendor/src/widgets/store-setup-checklist.tsx
84
+ import { useMemo, useState } from "react"
85
+ import { useNavigate } from "react-router-dom"
86
+ import { Container, Text, clx } from "@medusajs/ui"
87
+ import { CheckCircleSolid, TriangleDownMini, CircleDottedLine } from "@medusajs/icons"
88
+ import { Collapsible as RadixCollapsible } from "radix-ui"
89
+
90
+ const StoreSetupChecklist = ({ data: seller }: { data?: SellerDTO }) => {
91
+ const navigate = useNavigate()
92
+ const [open, setOpen] = useState(true)
93
+
94
+ const steps = useMemo(
95
+ () => (seller ? getProfileSteps(seller) : []),
96
+ [seller]
97
+ )
98
+
99
+ if (!seller) {
100
+ return null
101
+ }
102
+
103
+ const completedCount = steps.filter((s) => s.completed).length
104
+ const totalCount = steps.length
105
+ const progressPercent = (completedCount / totalCount) * 100
106
+
107
+ if (completedCount === totalCount) {
108
+ return null
109
+ }
110
+
111
+ return (
112
+ <RadixCollapsible.Root open={open} onOpenChange={setOpen}>
113
+ <Container className="overflow-hidden p-0">
114
+ <div
115
+ className="h-1 bg-ui-tag-green-icon transition-all duration-500"
116
+ style={{ width: `${progressPercent}%` }}
117
+ />
118
+ <div className="p-6">
119
+ <RadixCollapsible.Trigger asChild>
120
+ <button className="flex w-full items-center justify-between">
121
+ <Text size="large" weight="plus" leading="compact">
122
+ Complete store profile
123
+ </Text>
124
+ <TriangleDownMini
125
+ className={clx(
126
+ "text-ui-fg-muted transition-transform duration-200",
127
+ !open && "-rotate-90"
128
+ )}
129
+ />
130
+ </button>
131
+ </RadixCollapsible.Trigger>
132
+
133
+ <RadixCollapsible.Content>
134
+ <div className="mt-4 flex flex-col gap-y-3">
135
+ {steps.map((step) => (
136
+ <button
137
+ key={step.key}
138
+ className="flex items-center gap-x-3 text-left"
139
+ onClick={() => !step.completed && navigate(step.path)}
140
+ disabled={step.completed}
141
+ >
142
+ {step.completed ? (
143
+ <CheckCircleSolid className="text-ui-tag-green-icon shrink-0" />
144
+ ) : (
145
+ <CircleDottedLine className="text-ui-fg-muted shrink-0" />
146
+ )}
147
+ <Text size="small" leading="compact" className="text-ui-fg-base">
148
+ {step.label}
149
+ </Text>
150
+ </button>
151
+ ))}
152
+ </div>
153
+ </RadixCollapsible.Content>
154
+ </div>
155
+ </Container>
156
+ </RadixCollapsible.Root>
157
+ )
158
+ }
159
+ ```
160
+
161
+ </Step>
162
+ <Step title="Attach it to the store-setup zone">
163
+ Export the component as the **default** and a `config` with a `seller.setup`
164
+ zone. `before` renders it above the built-in card; use `after` to place it
165
+ below:
166
+
167
+ ```tsx apps/vendor/src/widgets/store-setup-checklist.tsx
168
+ import { defineWidgetConfig } from "@mercurjs/dashboard-sdk"
169
+
170
+ export const config = defineWidgetConfig({
171
+ zone: "seller.setup.before",
172
+ })
173
+
174
+ export default StoreSetupChecklist
175
+ ```
176
+
177
+ </Step>
178
+ </Steps>
179
+
180
+ ## Where it renders
181
+
182
+ `seller.setup` is hosted in two places, both passing the same `seller` as `data`:
183
+
184
+ | Host | When it shows |
185
+ | ---------------------------------------- | ------------------------------------------------------------ |
186
+ | The vendor shell (above the page outlet) | On top-level routes — the dashboard "home" onboarding banner |
187
+ | The store settings detail page | Always, above the store status banner |
188
+
189
+ A single widget file covers both. Multiple `seller.setup.before` / `.after`
190
+ widgets stack in registration order, so your checklist and the built-in card can
191
+ coexist — or hide the built-in one by rendering your own and letting theirs
192
+ complete.
193
+
194
+ <Note>
195
+ **No data fetching.** Because the zone hands you the resolved `seller`, the
196
+ widget is pure render — you never call the SDK. To *collect and persist* a new
197
+ onboarding value (e.g. a Tax ID) rather than just link to existing pages, see
198
+ [Extend the onboarding flow](/rc/resources/tutorials/extend-onboarding), which
199
+ carries a value through `additional_data` to a workflow hook.
200
+ </Note>
201
+
202
+ ## Next steps
203
+
204
+ <CardGroup cols={2}>
205
+ <Card
206
+ title="Extend the onboarding flow"
207
+ href="/rc/resources/tutorials/extend-onboarding"
208
+ >
209
+ Collect a new value and persist it through a workflow hook.
210
+ </Card>
211
+ <Card title="Add a widget" href="/rc/resources/tutorials/add-a-widget">
212
+ The full widget model and the published zone registry.
213
+ </Card>
214
+ </CardGroup>
package/llms.txt CHANGED
@@ -28,26 +28,39 @@ package (`node_modules/@mercurjs/docs/`).
28
28
  - [LLMs](content/resources/ai/llms.mdx) — Feed Mercur documentation directly to LLMs for accurate, context-aware assistance.
29
29
  - [MCP Server](content/resources/ai/mcp.mdx) — Connect your AI environment to Mercur documentation via Model Context Protocol.
30
30
  - [Building with AI](content/resources/ai/overview.mdx) — Mercur ships version-matched docs inside your project so AI coding agents build from accurate APIs instead of stale training data.
31
+ - [API routes](content/resources/best-practices/api-routes.mdx) — Thin HTTP adapters — typed request/response generics, Zod validation with createFindParams, filterable fields, middlewares as filters, and queryConfig.
32
+ - [Custom fields](content/resources/best-practices/custom-fields.mdx) — The full loop — add a custom field in core, render it in the panels with defineCustomFieldsConfig, pull linked data with the link property, and type it end-to-end.
33
+ - [Frontend](content/resources/best-practices/frontend.mdx) — Build panel UI the right way — @medusajs/ui usage, custom-field extensions (forms, tables, read-only section fields), and new pages, with the correct imports.
34
+ - [Module links](content/resources/best-practices/module-links.mdx) — Relate modules without coupling them — defineLink, the link-direction rule, built-in link steps, and filtering by links.
35
+ - [Modules](content/resources/best-practices/modules.mdx) — Keep modules thin — data access and CRUD only. Naming, decorators, and what must never live in a module service.
36
+ - [Best Practices Overview](content/resources/best-practices/overview.mdx) — How to build with Mercur — the layered architecture, the non-negotiable rules, and where each piece of logic belongs.
37
+ - [Subscribers & jobs](content/resources/best-practices/subscribers-and-jobs.mdx) — React to events and run periodic work safely — fetch from { id }, mutate via workflows, log-don't-throw, idempotency and loop guards, cron jobs.
38
+ - [Types & augmentation](content/resources/best-practices/types.mdx) — Type the panels against your own backend extensions — augment framework DTOs with declaration merging so every SDK endpoint carries your custom data.
39
+ - [Workflows](content/resources/best-practices/workflows.mdx) — Where all business logic and mutations live — composition-function constraints, one-mutation-per-step with compensation, reusing built-in steps, and the query engine.
31
40
  - [Custom Fields](content/resources/customization/custom-fields.mdx) — Extend any Medusa entity with additional fields without modifying core code.
32
41
  - [Extend a workflow](content/resources/customization/extend-a-workflow.mdx) — Inject custom logic into an existing Mercur workflow through hooks — without rewriting it.
33
- - [Extending Panels](content/resources/customization/extending-panels.mdx) — Add pages, customize navigation, branding, and replace components in the admin and vendor panels.
42
+ - [Extending Panels](content/resources/customization/extending-panels.mdx) — Add pages, inject widgets, customize navigation, and extend forms and tables in the admin and vendor panels.
34
43
  - [Medusa Cloud deployment](content/resources/deployment/medusa-cloud.mdx) — Deploy Mercur — backend, admin panel, and vendor panel — on Medusa Cloud.
35
44
  - [Notifications](content/resources/integrations/notifications.mdx) — Send transactional emails and marketplace notifications with Resend.
36
45
  - [Search](content/resources/integrations/search.mdx) — Mercur's provider-agnostic search module — zero-infrastructure Orama by default, swappable for Algolia, Meilisearch, or your own provider.
37
46
  - [Stripe Connect Integration](content/resources/integrations/stripe-connect.mdx) — Set up Stripe Connect for marketplace payments and seller payouts — from Stripe Dashboard configuration to the full end-to-end payment lifecycle.
38
47
  - [Add a feature with a block](content/resources/tutorials/add-a-block.mdx) — Install the reviews block end-to-end and see it live across the admin, vendor, and storefront surfaces.
48
+ - [Add a widget](content/resources/tutorials/add-a-widget.mdx) — Inject a React component at a named zone on a built-in panel page with defineWidgetConfig — no forking.
49
+ - [Add a button to order details](content/resources/tutorials/add-order-detail-button.mdx) — Drop a 'Copy link' button onto the vendor order detail page with a widget — no forking, no page override.
39
50
  - [Create attributes and variant axes](content/resources/tutorials/attributes-and-variant-axes.mdx) — Build the attribute catalog: a filterable attribute, a variant axis backed by a native product option, and an inline product-scoped axis.
40
51
  - [Build your own block](content/resources/tutorials/build-a-block.mdx) — Author a reusable feature as a block — backend, panel UI, and docs — build it into a registry, and install it into any Mercur project.
41
52
  - [Configure commissions](content/resources/tutorials/configure-commissions.mdx) — Set the global commission, add scoped rules for categories and sellers, and confirm the right rate lands on an order.
42
53
  - [Add a custom API route](content/resources/tutorials/custom-api-route.mdx) — Create a backend endpoint, regenerate the route map, and call it from a panel page with full type safety end to end.
43
54
  - [Add a custom panel page](content/resources/tutorials/custom-panel-page.mdx) — Add a page to the vendor portal with file-based routing — the dashboard SDK wires it in automatically.
55
+ - [Customize navigation](content/resources/tutorials/customize-navigation.mdx) — Reorder, hide, relabel, and re-parent built-in sidebar items with a single _navigation.ts file and defineNavigationConfig.
56
+ - [Extend forms and tables](content/resources/tutorials/extend-forms-and-tables.mdx) — Add validated fields to built-in forms, replace fields in detail sections, and add list columns with defineCustomFieldsConfig and createFormHelper.
57
+ - [Extend the onboarding flow](content/resources/tutorials/extend-onboarding.mdx) — Add a field to the vendor store-setup surface, submit it through additional_data, and persist it durably from a workflow hook — end to end, without forking.
44
58
  - [Build your first marketplace](content/resources/tutorials/first-marketplace.mdx) — Go end-to-end: create a project, approve a seller, list a product, place a split order, and watch a payout settle.
45
59
  - [Handle product requests](content/resources/tutorials/handle-product-requests.mdx) — Walk a vendor's product submission and a follow-up edit through the operator review pipeline — confirm, decline, or send back.
46
60
  - [Import and export products via CSV](content/resources/tutorials/import-export-products.mdx) — Install the product-import-export block and give vendors CSV import and export drawers on their product list.
47
61
  - [Work with master products and offers](content/resources/tutorials/master-products-and-offers.mdx) — Two sellers compete on one catalog entry: publish a master product, create competing offers, and read per-offer prices from the Store API.
48
- - [Re-compose a built-in page](content/resources/tutorials/recompose-a-page.mdx) — Override a built-in panel page by dropping a route file at the same path and re-composing its compound component slots.
49
- - [Replace panel components](content/resources/tutorials/replace-panel-components.mdx) — Swap the sidebar, topbar actions, or store setup screen for your own components through dashboard SDK configuration.
50
62
  - [Set up seller payouts](content/resources/tutorials/seller-payouts-stripe.mdx) — Take a seller from zero to paid: Stripe Connect onboarding, an order through fulfillment, capture, and the automated payout.
63
+ - [Build a store-setup checklist](content/resources/tutorials/store-setup-checklist.mdx) — Render an onboarding progress checklist on the vendor store-setup surface with a seller.setup widget — the seller is handed to you as data, no fetching required.
51
64
 
52
65
  ## Tools — CLI, API client, dashboard SDK
53
66
 
@@ -213,6 +226,7 @@ package (`node_modules/@mercurjs/docs/`).
213
226
  - [Search module](content/references/modules/search.mdx) — The provider-agnostic search module, its document shapes, and the provider contract.
214
227
  - [Seller module](content/references/modules/seller.mdx) — Data models, links, and service methods for the Seller module.
215
228
  - [References](content/references/overview.mdx) — Technical references for Mercur's modules, HTTP APIs, workflows, and configuration.
229
+ - [Panel Extension API](content/references/panel-extension-api.mdx) — The file-based helpers for extending the admin and vendor panels — defineWidgetConfig, defineNavigationConfig, defineCustomFieldsConfig, createFormHelper — and the exact props passed to every component you supply.
216
230
  - [addSellerShippingMethodToCartWorkflow](content/references/workflows/cart/add-seller-shipping-method-to-cart.mdx) — Add shipping methods to a cart, replacing only the same seller's existing methods.
217
231
  - [completeCartWithSplitOrdersWorkflow](content/references/workflows/cart/complete-cart-with-split-orders.mdx) — Complete a multi-seller cart by splitting it into per-seller orders under an order group.
218
232
  - [listSellerShippingOptionsForCartWorkflow](content/references/workflows/cart/list-seller-shipping-options-for-cart.mdx) — List available shipping options for a cart, grouped by seller.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mercurjs/docs",
3
- "version": "2.2.0-rc.0",
3
+ "version": "2.2.0-rc.2",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/mercurjs/mercur",
@@ -1,129 +0,0 @@
1
- ---
2
- title: "Re-compose a built-in page"
3
- description: "Override a built-in panel page by dropping a route file at the same path and re-composing its compound component slots."
4
- ---
5
-
6
- Every page in the admin and vendor panels is exported as a **compound component** — a root plus named slots like `Header`, `HeaderActions`, and `DataTable`. That means you never fork a page to change one part of it: you drop a `page.tsx` at the same route path, render the original page, and swap only the slot you care about. Everything you don't touch keeps its default behavior — data fetching, filters, pagination, i18n, all of it.
7
-
8
- <Info>
9
- **This is not Medusa's widget system.** Medusa's admin lets you inject widgets into predefined zones around a page. Mercur takes a different approach: pages are React compound components you re-compose directly. There are no injection zones and no `defineWidgetConfig` — you get the actual page component and decide what renders inside it. See [Extending Panels](/rc/resources/customization/extending-panels) for the full comparison.
10
- </Info>
11
-
12
- ## What you'll build
13
-
14
- A vendor portal `/products` page with **Import** and **Export** buttons added next to the built-in Create button — while the table, search, filters, and everything else stay exactly as shipped. This is the same technique the official `product-import-export` block uses.
15
-
16
- ## Slot anatomy
17
-
18
- Built-in pages are exported from the `/pages` subpath of each panel package:
19
-
20
- ```typescript
21
- import { ProductListPage } from "@mercurjs/vendor/pages" // vendor portal
22
- import { CustomerListPage } from "@mercurjs/admin/pages" // admin panel
23
- ```
24
-
25
- Each list page exposes the same family of slots:
26
-
27
- | Slot | Renders |
28
- |------|---------|
29
- | `Table` | The `Container` shell holding header + table |
30
- | `Header` | The title/actions row |
31
- | `HeaderTitle` | Heading and subtitle |
32
- | `HeaderActions` | The action button cluster |
33
- | `HeaderCreateButton` | The built-in Create button |
34
- | `DataTable` | The wired data table — fetching, columns, filters, pagination |
35
-
36
- Detail pages expose section slots instead (e.g. `CustomerDetailPage.Main`, `CustomerDetailPage.Sidebar`, `CustomerDetailPage.MainGeneralSection`).
37
-
38
- ## Override the page
39
-
40
- <Steps>
41
- <Step title="Drop a route file at the same path">
42
- Create the file in your vendor app at the path that matches the built-in route. `/products` maps to `src/routes/products/page.tsx`:
43
-
44
- ```tsx apps/vendor/src/routes/products/page.tsx
45
- import { Link } from "react-router-dom"
46
- import { Button } from "@medusajs/ui"
47
- import { ArrowDownTray, ArrowUpTray } from "@medusajs/icons"
48
- import { ProductListPage } from "@mercurjs/vendor/pages"
49
-
50
- export default function ProductsWithImportExport() {
51
- return (
52
- <ProductListPage>
53
- <ProductListPage.Table>
54
- <ProductListPage.Header>
55
- <ProductListPage.HeaderTitle />
56
- <ProductListPage.HeaderActions>
57
- <Button size="small" variant="secondary" asChild>
58
- <Link to="import">
59
- <ArrowUpTray />
60
- Import
61
- </Link>
62
- </Button>
63
- <Button size="small" variant="secondary" asChild>
64
- <Link to="export">
65
- <ArrowDownTray />
66
- Export
67
- </Link>
68
- </Button>
69
- <ProductListPage.HeaderCreateButton />
70
- </ProductListPage.HeaderActions>
71
- </ProductListPage.Header>
72
- <ProductListPage.DataTable />
73
- </ProductListPage.Table>
74
- </ProductListPage>
75
- )
76
- }
77
- ```
78
- </Step>
79
- <Step title="Reuse the slots you don't change">
80
- Note what happened in that file: `HeaderTitle`, `HeaderCreateButton`, and `DataTable` are the originals, rendered untouched. Only `HeaderActions` gained two buttons. You re-compose top-down — render the page, re-declare only the branch you're changing, and keep original slots for everything inside it.
81
- </Step>
82
- <Step title="Reload the panel">
83
- The dashboard SDK picks the file up automatically (adding or removing a route file triggers a full reload in dev). Because the path matches a built-in route, **your page replaces the built-in one** — no configuration, no registration.
84
- </Step>
85
- </Steps>
86
-
87
- <Note>
88
- When a custom route's path matches a built-in route, your page replaces the built-in one. Routes at new paths are appended instead. That one rule is the entire override mechanism.
89
- </Note>
90
-
91
- ## How defaults work
92
-
93
- If you render a compound page with **no children**, it renders its full default composition — so `<ProductListPage />` alone is identical to the built-in page. Every page root follows the pattern:
94
-
95
- ```tsx
96
- Children.count(children) > 0 ? children : <DefaultComposition />
97
- ```
98
-
99
- ## Verify
100
-
101
- 1. Run your project (`bun run dev`) and open the vendor portal.
102
- 2. Navigate to **Products** — the list should look identical to before, plus Import and Export buttons in the header.
103
- 3. Search, filter, and paginate the table — all built-in behavior must still work, since `DataTable` is untouched.
104
- 4. Delete your `src/routes/products/page.tsx` and reload — the built-in page comes back. Nothing was forked.
105
-
106
- ## FAQ
107
-
108
- <AccordionGroup>
109
- <Accordion title="Where do I find which slots a page exposes?">
110
- Import the page from `@mercurjs/vendor/pages` or `@mercurjs/admin/pages` and let your editor's autocomplete list the attached members — every slot is a static property on the page component. The naming is consistent across pages: list pages expose `Table`/`Header`/`HeaderTitle`/`HeaderActions`/`HeaderCreateButton`/`DataTable`; detail pages expose `Main`/`Sidebar` plus one property per section.
111
- </Accordion>
112
- <Accordion title="Can I remove a built-in element, like the Create button?">
113
- Yes — re-composition is declarative. Anything you don't render doesn't appear: re-declare `HeaderActions` with only your own buttons and omit `HeaderCreateButton`.
114
- </Accordion>
115
- <Accordion title="What about changing the table columns?">
116
- `DataTable` is a single slot — you either keep it wholesale or replace it with your own table. For a different column set, replace the slot with your own component built on the shared `DataTable`/`useDataTable` primitives from the panel package.
117
- </Accordion>
118
- </AccordionGroup>
119
-
120
- ## Next steps
121
-
122
- <CardGroup cols={2}>
123
- <Card title="Replace panel components" href="/rc/resources/tutorials/replace-panel-components">
124
- Swap global chrome like the sidebar or topbar instead of a single page.
125
- </Card>
126
- <Card title="Add a custom API route" href="/rc/resources/tutorials/custom-api-route">
127
- Back your custom page with a typed endpoint of your own.
128
- </Card>
129
- </CardGroup>
@@ -1,116 +0,0 @@
1
- ---
2
- title: "Replace panel components"
3
- description: "Swap the sidebar, topbar actions, or store setup screen for your own components through dashboard SDK configuration."
4
- ---
5
-
6
- Some customizations aren't about one page — they change the panel's global chrome. For those, the dashboard SDK exposes a small set of **named component overrides**: you point the plugin config at your own file, and the panel renders your component in place of the built-in one everywhere it appears.
7
-
8
- <Info>
9
- **Choose the right tool.** A component override replaces one of four global layout slots across the whole panel. If you want to change a single page, [re-compose it](/rc/resources/tutorials/recompose-a-page) instead — and if you want to add a new screen, just [drop in a route](/rc/resources/tutorials/custom-panel-page). The decision guide in [Extending Panels](/rc/resources/customization/extending-panels#choosing-your-extension-mechanism) compares all three.
10
- </Info>
11
-
12
- ## What you'll build
13
-
14
- A vendor portal with custom topbar actions (a help link and a status badge) and a custom main sidebar, configured entirely from `vite.config.ts`.
15
-
16
- ## The available slots
17
-
18
- Exactly four layout components can be replaced:
19
-
20
- | Component | Where it renders |
21
- |-----------|------------------|
22
- | `MainSidebar` | Primary navigation sidebar on every main page |
23
- | `SettingsSidebar` | Sidebar of the `/settings` section |
24
- | `TopbarActions` | Action cluster on the right side of the top bar |
25
- | `StoreSetup` | The store setup screen shown to new sellers (vendor portal) |
26
-
27
- Anything else — a page, a section, a table — is customized through routes and compound components, not through overrides.
28
-
29
- ## Set up the override
30
-
31
- <Steps>
32
- <Step title="Write the replacement component">
33
- Create the component anywhere under `src/`. It must have a **default export**:
34
-
35
- ```tsx apps/vendor/src/components/topbar-actions.tsx
36
- import { Badge, Button } from "@medusajs/ui"
37
-
38
- export default function TopbarActions() {
39
- return (
40
- <div className="flex items-center gap-x-2">
41
- <Badge size="2xsmall" color="green">
42
- All systems operational
43
- </Badge>
44
- <Button size="small" variant="transparent" asChild>
45
- <a href="https://help.example.com" target="_blank" rel="noreferrer">
46
- Help
47
- </a>
48
- </Button>
49
- </div>
50
- )
51
- }
52
- ```
53
- </Step>
54
- <Step title="Register it in the Vite config">
55
- Component overrides live in the `components` option of `mercurDashboardPlugin`, in your panel app's `vite.config.ts`. Paths are **relative to `src/`**:
56
-
57
- ```typescript apps/vendor/vite.config.ts
58
- import { defineConfig } from 'vite'
59
- import react from '@vitejs/plugin-react'
60
- import { mercurDashboardPlugin } from '@mercurjs/dashboard-sdk'
61
-
62
- export default defineConfig({
63
- plugins: [
64
- react(),
65
- mercurDashboardPlugin({
66
- medusaConfigPath: '../../packages/api/medusa-config.ts',
67
- components: {
68
- TopbarActions: 'components/topbar-actions',
69
- MainSidebar: 'components/main-sidebar',
70
- },
71
- }),
72
- ],
73
- })
74
- ```
75
-
76
- The plugin resolves each path at build time and swaps your component in through the `virtual:mercur/components` module — no runtime registration, no context providers to wire.
77
- </Step>
78
- <Step title="Restart the dev server">
79
- Vite config changes require a restart. After it, your components render everywhere their slots appear.
80
- </Step>
81
- </Steps>
82
-
83
- <Tip>
84
- Build overrides with `@medusajs/ui` components and Medusa UI color tokens so they match the rest of the panel. The panels ship no other UI library.
85
- </Tip>
86
-
87
- ## Verify
88
-
89
- 1. Open the vendor portal — the top bar shows your badge and Help link on every page.
90
- 2. Navigate between main pages and `/settings` — the override applies everywhere its slot renders.
91
- 3. Remove the `components` entry and restart — the built-in components return. The originals were never modified.
92
-
93
- ## FAQ
94
-
95
- <AccordionGroup>
96
- <Accordion title="If I replace MainSidebar, do my custom routes still show up in the menu?">
97
- Only if your sidebar renders them. A `MainSidebar` override replaces the **entire** navigation sidebar, including the auto-generated menu items from route `config` exports — read them from `virtual:mercur/menu-items` if you want to keep automatic navigation. If you only want to add or reorder items, you don't need an override at all: the `config` export on route files (with `rank` and `nested`) covers that.
98
- </Accordion>
99
- <Accordion title="Can I override other components, like the login page or a form?">
100
- No — these four slots are the complete list. The login flow, forms, and everything page-level are customized through drop-in routes and [compound re-composition](/rc/resources/tutorials/recompose-a-page).
101
- </Accordion>
102
- <Accordion title="Do overrides apply to both panels?">
103
- Each panel app has its own `vite.config.ts`, so overrides are configured per panel. `StoreSetup` only exists in the vendor portal; the other three slots exist in both.
104
- </Accordion>
105
- </AccordionGroup>
106
-
107
- ## Next steps
108
-
109
- <CardGroup cols={2}>
110
- <Card title="Re-compose a built-in page" href="/rc/resources/tutorials/recompose-a-page">
111
- Change one page's composition instead of global chrome.
112
- </Card>
113
- <Card title="Extending Panels" href="/rc/resources/customization/extending-panels">
114
- The full reference for routes, navigation, branding, and overrides.
115
- </Card>
116
- </CardGroup>