
Building Admin Dashboard Customizations
Extend the Medusa admin dashboard with correct TanStack Query patterns, Medusa JS SDK calls, and cache-safe mutations for custom widgets and routes.
Overview
Building Admin Dashboard Customizations is an agent skill for the Build phase that guides Medusa admin extensions using the JS SDK and TanStack Query patterns.
Install
npx skills add https://github.com/medusajs/medusa-agent-skills --skill building-admin-dashboard-customizationsWhat is this skill?
- Fundamental rules: always use Medusa JS SDK, never raw fetch for admin API calls
- TanStack Query patterns for basic, paginated, dependent, and multi-ID queries
- useMutation recipes with loading state plus create and delete flows
- Cache invalidation guidelines and metadata update patterns
- Common issues section plus a complete widget example with separate queries
Adoption & trust: 2.2k installs on skills.sh; 185 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
Your Medusa admin widgets break caching, pagination, or auth because the agent used fetch() and ad-hoc React data hooks.
Who is it for?
Indie commerce builders customizing Medusa admin with AI assistance who need SDK-first TanStack Query recipes.
Skip if: Non-Medusa stacks, storefront-only Next.js work, or backend-only Medusa module development without admin UI.
When should I use this skill?
Adding or fixing Medusa admin dashboard customizations that fetch or mutate data via the admin API.
What do I get? / Deliverables
Custom admin routes and widgets load and mutate data through SDK-backed queries and documented invalidation patterns consistent with Medusa admin.
- Admin widget or route code following SDK and Query patterns
- Documented cache invalidation strategy after mutations
Recommended Skills
Journey fit
Admin UI customizations are implemented while building the commerce product, before storefront and ops workflows go live. The skill documents React admin UI data loading—not Medusa server modules or deployment—so it shelves under frontend admin work.
How it compares
Medusa-specific admin data-loading skill—not generic React Query docs or Store API storefront guides.
Common Questions / FAQ
Who is building-admin-dashboard-customizations for?
Developers extending Medusa’s admin dashboard with custom widgets, routes, or settings screens alongside an AI coding agent.
When should I use building-admin-dashboard-customizations?
During Build while implementing admin UI that lists, searches, paginates, or mutates Medusa entities through the admin API.
Is building-admin-dashboard-customizations safe to install?
Treat it as documentation-heavy guidance; confirm SDK versions match your Medusa project and review the Security Audits panel on this page before granting repo access.
SKILL.md
READMESKILL.md - Building Admin Dashboard Customizations
# Data Loading Principles and Patterns ## Contents - [Fundamental Rules](#fundamental-rules) - [Think Before You Code Checklist](#think-before-you-code-checklist) - [Common Mistake vs Correct Pattern](#common-mistake-vs-correct-pattern) - [Working with Tanstack Query](#working-with-tanstack-query) - [Fetching Data with useQuery](#fetching-data-with-usequery) - [Basic Query](#basic-query) - [Paginated Query](#paginated-query) - [Query with Dependencies](#query-with-dependencies) - [Fetching Multiple Items by IDs](#fetching-multiple-items-by-ids) - [Updating Data with useMutation](#updating-data-with-usemutation) - [Basic Mutation](#basic-mutation) - [Mutation with Loading State](#mutation-with-loading-state) - [Create Mutation](#create-mutation) - [Delete Mutation](#delete-mutation) - [Cache Invalidation Guidelines](#cache-invalidation-guidelines) - [Important Notes about Metadata](#important-notes-about-metadata) - [Common Patterns](#common-patterns) - [Pattern: Fetching Data with Pagination](#pattern-fetching-data-with-pagination) - [Pattern: Search with Debounce](#pattern-search-with-debounce) - [Pattern: Updating Metadata with useMutation](#pattern-updating-metadata-with-usemutation) - [Common Issues & Solutions](#common-issues--solutions) - [Complete Example: Widget with Separate Queries](#complete-example-widget-with-separate-queries) ## Fundamental Rules 1. **ALWAYS use the Medusa JS SDK** - NEVER use regular fetch() for API requests (missing headers causes authentication/authorization errors) 2. **Display data must load on mount** - Any data shown in the widget's main UI must be fetched when the component mounts, not conditionally 3. **Separate concerns** - Modal/form data queries should be independent from display data queries 4. **Handle reference data properly** - When storing IDs/references (in metadata or elsewhere), you must fetch the full entities to display them 5. **Always show loading states** - Users should see loading indicators, not empty states, while data is being fetched 6. **Invalidate the right queries** - After mutations, invalidate the queries that provide display data, not just the modal queries ## Think Before You Code Checklist Before implementing any widget that displays data: - [ ] Am I using the Medusa JS SDK for all API requests (not regular fetch)? - [ ] For built-in endpoints, am I using existing SDK methods (not sdk.client.fetch)? - [ ] What data needs to be visible immediately? - [ ] Where is this data stored? (metadata, separate endpoint, related entities) - [ ] If storing IDs, how will I fetch the full entities for display? - [ ] Are my display queries separate from interaction queries? - [ ] Have I added loading states for all data fetches? - [ ] Which queries need invalidation after updates to refresh the display? ## Common Mistake vs Correct Pattern ### ❌ WRONG - Single query for both display and modal: ```tsx // This breaks on page refresh! const { data } = useQuery({ queryFn: () => sdk.admin.product.list(), enabled: modalOpen, // Display won't work on mount! }) // Trying to display filtered data from modal query const displayItems = data?.filter((item) => ids.includes(item.id)) // No data until modal opens ``` **Why this is wrong:** - On page refresh, modal is closed, so query doesn't run - User sees empty state instead of their data - Display depends on modal interaction ### ✅ CORRECT - Separate queries with proper invalidation: ```tsx // Display data - loads immediately const { data: displayData } = useQuery({ queryFn: () => fetchDisplayData(), queryKey: ["display-data", product.id], // No 'enabled' condition - loads on mount }) // Modal data - loads when needed const { data: modalData } = useQuery({ queryFn: () => fetchModalData(), queryKey: ["modal-data"], enabled: modalOpen, // OK for modal-only data }) // Mutation with proper cache invalidation const updateMutation = useMutation({ mutationFn: updateFunction, onSuccess: (