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
By medusajs · 3,791 installs
npx skills add medusajs/medusa-agent-skills --skill building-admin-dashboard-customizations
Source repository · Upstream listing
Medusa 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).
When to Apply
Load this skill for ANY admin UI development task, including:
Creating widgets for product/order/customer pages
Building custom admin pages
Implementing forms and modals
Displaying data with tables or lists
Adding navigation between pages
Also load these skills when:
building with medusa: Building backend API routes that the admin UI calls
building storefronts: If working on storefront instead of admin dashboard
CRITICAL: Load Reference Files When Needed
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:
Creating widgets? → MUST load references/data loading.md first
Building forms/modals? → MUST load references/forms.md first
Displaying data in tables/lists? → MUST load references/display patterns.md first
Selecting from large datasets? → MUST load references/table selection.md first
Adding navigation? → MUST load references/navigation.md first
Styling components? → MUST load references/typography.md first
Minimum requirement: Load at least 1 2 reference files relevant to your specific task before implementing.
When to Use This Skill vs MedusaDocs MCP Server
⚠️ CRITICAL: This skill should be consulted FIRST for planning and implementation.
Use this skill for (PRIMARY SOURCE):
Planning Understanding how to structure admin UI features
Component patterns Widgets, pages, forms, tables, modals
Design system Typography, colors, spacing, semantic classes
Data loading Critical separate query pattern, cache invalidation
Best practices Correct vs incorrect patterns (e.g., display queries on mount)
Critical rules What NOT to do (common mistakes like conditional display queries)
Use MedusaDocs MCP server for (SECONDARY SOURCE):
Specific component prop signatures after you know which component to use
Available widget zones list
JS SDK method details
Configuration options reference
Why skills come first:
Skills contain critical patterns like separate display/modal queries that MCP doesn't emphasize
Skills show correct vs incorrect patterns; MCP shows what's possible
Planning requires understanding patterns, not just API reference
Critical Setup Rules
SDK Client Configuration
CRITICAL: Always use exact configuration different values cause errors:
pnpm Users ONLY
CRITICAL: Install peer dependencies BEFORE writing any code:
npm/yarn users: DO NOT install these packages already available.
Rule Categories by Priority
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
Quick Reference
1. Data Loading (CRITICAL)
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 routes
data 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 queries
data invalidate display Invalidate display queries after mutations, not just modal queries
data loading states Always show loading states (Spinner), not empty states
data pnpm install first pnpm users MUST install @tanstack/react query BEFORE coding
2. Design System (CRITICAL)
design semantic colors Always use semantic color classes (bg ui bg base, text ui fg subtle), never hardcoded
design spacing Use px 6 py 4 for section padding, gap 2 for lists, gap 3 for items
design button size Always use size="small" for buttons in widgets and tables
design medusa components Always use Medusa UI components (Container, Button, Text), not raw HTML
3. Data Display (HIGH)
display price format CRITICAL : Prices from Medusa are stored as is ($49.99 = 49.99, NOT in cents). Display them directly NEVER divide by 100
4. Typography (HIGH)
typo text component Always use Text component from @medusajs/ui, never plain span/p tags
typo labels Use <Text size="small" leading="compact" weight="plus" for labels/headings
typo descriptions Use <Text size="small" leading="compact" className="text ui fg subtle" for descriptions
typo no heading widgets Never use Heading for small sections in widgets (use Text instead)
5. Forms & Modals (MEDIUM)
form focusmodal create Use FocusModal for creating new entities
form drawer edit Use Drawer for editing existing entities
form disable pending Always disable actions during mutations (disabled={mutation.isPending})
form show loading Show loading state on submit button (isLoading={mutation.isPending})
6. Selection Patterns (MEDIUM)
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" error
Critical Data Loading Pattern
ALWAYS follow this pattern never load display data conditionally:
Why this matters:
On page refresh, modal is closed, so conditional query doesn't run
User sees empty state instead of their data
Display depends on modal interaction (broken UX)
Common Mistakes Checklist
Before implementing, verify you're NOT doing these:
Data Loading:
[ ] Using regular fetch() instead of Medusa JS SDK (causes missing auth header errors)
[ ] Not using existing SDK methods for built in endpoints (e.g., using sdk.client.fetch("/admin/products") instead of sdk.admin.product.list())
[ ] Loading display data conditionally based on modal/UI state
[ ] Using a single query for both display and modal
[ ] Forgetting to invalidate display queries after mutations
[ ] Not handling loading states (showing empty instead of spinner)
[ ] pnpm users: Not installing @tanstack/react query before coding
Design System:
[ ] Using hardcoded colors instead of semantic classes
[ ] Forgetting size="small" on buttons in widgets
[ ] Not using px 6 py 4 for section padding
[ ] Using raw HTML elements instead of Medusa UI components
Data Display:
[ ] CRITICAL : Dividing prices by 100 when displaying (prices are stored as is: $49.99 = 49.99, NOT in cents)
Typography:
[ ] Using plain span/p tags instead of Text component
[ ] Not using weight="plus" for labels
[ ] Not using text ui fg subtle for descriptions
[ ] Using Heading in small widget sections
Forms:
[ ] Using Drawer for creating (should use FocusModal)
[ ] Using FocusModal for editing (should use Drawer)
[ ] Not disabling buttons during mutations
[ ] Not showing loading state on submit
Selection:
[ ] Using DataTable for <10 items (overkill)
[ ] Using Select for 10 items (poor UX)
[ ] Not configuring search in useDataTable (causes error)
Reference Files Available
Load these for detailed patterns:
Each reference contains:
Step by step implementation guides
Correct vs incorrect code examples
Common mistakes and solutions
Complete working examples
Integration with Backend
⚠️ 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:
Admin routes need Authorization and session cookie headers
Store routes need x publishable api key header
SDK handles all required headers automatically
Regular fetch() without headers → authentication/authorization errors
Using existing SDK methods provides better type safety
When to use what:
Built in endpoints : Use existing SDK methods ( sdk.admin.product.list() , sdk.store.product.list() )
Custom endpoints : Use sdk.client.fetch() for your custom API routes
For implementing backend API routes , load the building with medusa skill.
Widget vs UI Route
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.
Don't promise the user a specific position based on the suffix. Say "the widget appears in the product details page and can be repositioned in the Editor view".
.side is still meaningful — it targets the side column of two column page layouts.
For new widgets, prefer the unsuffixed zone (e.g. 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 :
Name zones {resource}.{page context} (e.g. brand.list , brand.details ), plus .side for the side section. Never add .before / .after .
Register the zones in the 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.
A build warning about an unknown zone is expected for custom zones — verify the zone name is spelled correctly rather than ignoring it blindly.
Common Issues & Solutions
"Cannot find module" errors (pnpm users):
Install peer dependencies BEFORE coding
Use exact versions from dashboard
"No QueryClient set" error:
pnpm: Install @tanstack/react query
npm/yarn: Remove incorrectly installed package
"DataTable.Search not enabled":
Must pass search configuration to useDataTable
Widget not refreshing:
Invalidate display queries, not just modal queries
Include all dependencies in query keys
Display empty on refresh:
Display query has conditional enabled based on UI state
Remove condition display data must load on mount
Next Steps Testing Your Implementation
After successfully implementing a feature, always provide these next steps to the user:
1. Start the Development Server
If the server isn't already running, start it:
2. Access the Admin Dashboard
Open your browser and navigate to:
Admin Dashboard: http://localhost:9000/app
Log in with your admin credentials.
3. Navigate to Your Custom UI
For Widgets:
Navigate to the page where y