npx skills add ...
npx skills add medusajs/medusa-claude-plugins --skill building-admin-dashboard-customizations
Load automatically when planning, researching, or implementing Medusa Admin dashboard UI (widgets, custom pages, forms, tables, data loading, navigation). REQUIRED for all admin UI work in ALL modes (planning, implementation, exploration). Contains design patterns, component usage, and data loading patterns that MCP servers don't provide.
This repo is now called medusajs/medusa-agent-skills. Both names install the same content, but the install count here only covers this one.
npx skills add medusajs/medusa-claude-plugins --skill building-admin-dashboard-customizations
Build custom UI extensions for the Medusa Admin dashboard using the Admin SDK and Medusa UI components.
Note: "UI Routes" are custom admin pages, different from backend API routes (which use building-with-medusa skill).
Load this skill for ANY admin UI development task, including:
Also load these skills when:
The quick reference below is NOT sufficient for implementation. You MUST load relevant reference files before writing code for that component.
Load these references based on what you're implementing:
references/data-loading.md firstreferences/forms.md firstreferences/display-patterns.md firstreferences/table-selection.md firstreferences/navigation.md firstreferences/typography.md firstMinimum requirement: Load at least 1-2 reference files relevant to your specific task before implementing.
⚠️ CRITICAL: This skill should be consulted FIRST for planning and implementation.
Use this skill for (PRIMARY SOURCE):
Use MedusaDocs MCP server for (SECONDARY SOURCE):
Why skills come first:
CRITICAL: Always use exact configuration - different values cause errors:
CRITICAL: Install peer dependencies BEFORE writing any code:
npm/yarn users: DO NOT install these packages - already available.
| Priority | Category | Impact | Prefix |
|---|---|---|---|
| 1 | Data Loading | CRITICAL | data- |
| 2 | Design System | CRITICAL | design- |
| 3 | Data Display | HIGH (includes CRITICAL price rule) | display- |
| 4 | Typography | HIGH | typo- |
| 5 | Forms & Modals | MEDIUM | form- |
| 6 | Selection Patterns | MEDIUM | select- |
data-sdk-always - ALWAYS use Medusa JS SDK for ALL API requests - NEVER use regular fetch() (missing auth headers causes errors)data-sdk-method-choice - Use existing SDK methods for built-in endpoints (sdk.admin.product.list()), use sdk.client.fetch() for custom routesdata-display-on-mount - Display queries MUST load on mount (no enabled condition based on UI state)data-separate-queries - Separate display queries from modal/form queriesdata-invalidate-display - Invalidate display queries after mutations, not just modal queriesdata-loading-states - Always show loading states (Spinner), not empty statesdata-pnpm-install-first - pnpm users MUST install @tanstack/react-query BEFORE codingdesign-semantic-colors - Always use semantic color classes (bg-ui-bg-base, text-ui-fg-subtle), never hardcodeddesign-spacing - Use px-6 py-4 for section padding, gap-2 for lists, gap-3 for itemsdesign-button-size - Always use size="small" for buttons in widgets and tablesdesign-medusa-components - Always use Medusa UI components (Container, Button, Text), not raw HTMLdisplay-price-format - CRITICAL: Prices from Medusa are stored as-is ($49.99 = 49.99, NOT in cents). Display them directly - NEVER divide by 100typo-text-component - Always use Text component from @medusajs/ui, never plain span/p tagstypo-labels - Use <Text size="small" leading="compact" weight="plus"> for labels/headingstypo-descriptions - Use <Text size="small" leading="compact" className="text-ui-fg-subtle"> for descriptionstypo-no-heading-widgets - Never use Heading for small sections in widgets (use Text instead)form-focusmodal-create - Use FocusModal for creating new entitiesform-drawer-edit - Use Drawer for editing existing entitiesform-disable-pending - Always disable actions during mutations (disabled={mutation.isPending})form-show-loading - Show loading state on submit button (isLoading={mutation.isPending})select-small-datasets - Use Select component for 2-10 options (statuses, types, etc.)select-large-datasets - Use DataTable with FocusModal for large datasets (products, categories, etc.)select-search-config - Must pass search configuration to useDataTable to avoid "search not enabled" errorALWAYS follow this pattern - never load display data conditionally:
Why this matters:
Before implementing, verify you're NOT doing these:
Data Loading:
Design System:
Data Display:
Typography:
Forms:
Selection:
Load these for detailed patterns:
Each reference contains:
⚠️ CRITICAL: ALWAYS use the Medusa JS SDK for ALL API requests - NEVER use regular fetch()
Admin UI connects to backend API routes using the SDK:
Why the SDK is required:
Authorization and session cookie headersx-publishable-api-key headerWhen to use what:
sdk.admin.product.list(), sdk.store.product.list())sdk.client.fetch() for your custom API routesFor implementing backend API routes, load the building-with-medusa skill.
Widgets extend existing admin pages:
⚠️ .before / .after no longer control placement (v2.17.2+):
Since the Layout Composer landed, admin users arrange components — including widgets — through the dashboard's Editor view, and the arrangement is persisted in the database. The .before and .after zone suffixes are deprecated: a widget in product.details.before and one in product.details.after land in the same injection zone, and the final order is whatever the user configured.
.side is still meaningful — it targets the side column of two-column page layouts.product.details) unless the project already standardizes on a suffix.Newer zones added in v2.16.0 cover draft orders, gift cards, and store credit accounts (draft_order.*, gift_card.*, store_credit_account.*, in details/list/side variants). Ask the MedusaDocs MCP server for the authoritative zone list rather than guessing a zone name.
UI Routes create new admin pages:
Browser tab title (v2.17.2+): by default a UI route's tab title is its label. Export a handle with an seo resolver to override it, including dynamically from the route's loader data:
If seo returns no title, the dashboard falls back to the sidebar label, then the breadcrumb, then Medusa.
Custom injection zones (v2.16.0+): custom pages — most usefully in plugins — can expose their own widget injection zones by laying the page out with LayoutComposer from @medusajs/dashboard/components:
{resource}.{page-context} (e.g. brand.list, brand.details), plus .side for the side section. Never add .before/.after.InjectionZoneRegistry interface for type checking and autocompletion in defineWidgetConfig, and include "../../.medusa/types/augmentation-refs.d.ts" in src/admin/tsconfig.json's include array."Cannot find module" errors (pnpm users):
"No QueryClient set" error:
"DataTable.Search not enabled":
Widget not refreshing:
Display empty on refresh:
enabled based on UI stateAfter successfully implementing a feature, always provide these next steps to the user:
If the server isn't already running, start it:
Open your browser and navigate to:
Log in with your admin credentials.
For Widgets: Navigate to the page where your widget is displayed. Common widget zones:
Its exact position within the page is controlled by the user in the dashboard's Editor view (Layout Composer), not by the zone's .before/.after suffix. Tell the user they can drag the widget where they want it.
For UI Routes (Custom Pages):
label you configured)http://localhost:9000/app/[your-route-path]Depending on what was implemented, test:
Always present next steps in a clear, actionable format after implementation: