---
name: building-storefronts
description: >-
  Use when user says "Medusa storefront", "call Medusa API", "SDK integration", or "React Query". Required for storefront fetching, mutations, cache, Medusa API calls.
---

# Medusa Storefront Development

Frontend integration guide for building storefronts with Medusa. Covers SDK usage, React Query patterns, and calling custom API routes.

## When to Apply

**Load this skill for ANY storefront development task, including:**
- Calling custom Medusa API routes from the storefront
- Integrating Medusa SDK in frontend applications
- Using React Query for data fetching
- Implementing mutations with optimistic updates
- Error handling and cache invalidation

**Also load building-with-medusa when:** Building the backend API routes that the storefront calls

## CRITICAL: Load Reference Files When Needed

**The quick reference below is NOT sufficient for implementation.** You MUST load the reference file before writing storefront integration code.

**Load this reference when implementing storefront features:**

- **Calling API routes?** → MUST load `references/frontend-integration.md` first
- **Using SDK?** → MUST load `references/frontend-integration.md` first
- **Implementing React Query?** → MUST load `references/frontend-integration.md` first

## Rule Categories by Priority

| Priority | Category | Impact | Prefix |
|----------|----------|--------|--------|
| 1 | SDK Usage | CRITICAL | `sdk-` |
| 2 | React Query Patterns | HIGH | `query-` |
| 3 | Data Display | HIGH (includes CRITICAL price rule) | `display-` |
| 4 | Error Handling | MEDIUM | `error-` |

## Quick Reference

### 1. SDK Usage (CRITICAL)

- `sdk-always-use` - **ALWAYS use the Medusa JS SDK for ALL API requests** - NEVER use regular fetch()
- `sdk-existing-methods` - For built-in endpoints, use existing SDK methods (`sdk.store.product.list()`, `sdk.admin.order.retrieve()`)
- `sdk-client-fetch` - For custom API routes, use `sdk.client.fetch()`
- `sdk-required-headers` - SDK automatically adds required headers (publishable API key for store, auth for admin) - regular fetch() missing these headers causes errors
- `sdk-no-json-stringify` - **NEVER use JSON.stringify() on body** - SDK handles serialization automatically
- `sdk-plain-objects` - Pass plain JavaScript objects to body, not strings
- `sdk-locate-first` - Always locate where SDK is instantiated in the project before using it

### 2. React Query Patterns (HIGH)

- `query-use-query` - Use `useQuery` for GET requests (data fetching)
- `query-use-mutation` - Use `useMutation` for POST/DELETE requests (mutations)
- `query-invalidate` - Invalidate queries in `onSuccess` to refresh data after mutations
- `query-keys-hierarchical` - Structure query keys hierarchically for effective cache management
- `query-loading-states` - Always handle `isLoading`, `isPending`, `isError` states

### 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. Error Handling (MEDIUM)

- `error-on-error` - Implement `onError` callback in mutations to handle failures
- `error-display` - Show error messages to users when mutations fail
- `error-rollback` - Use optimistic updates with rollback on error for better UX

## Critical SDK Pattern

**ALWAYS pass plain objects to the SDK - NEVER use JSON.stringify():**

```typescript
// ✅ CORRECT - Plain object
await sdk.client.fetch("/store/reviews", {
  method: "POST",
  body: {
    product_id: "prod_123",
    rating: 5,
  }
})

// ❌ WRONG - JSON.stringify breaks the request
await sdk.client.fetch("/store/reviews", {
  method: "POST",
  body: JSON.stringify({  // ❌ DON'T DO THIS!
    product_id: "prod_123",
    rating: 5,
  })
})
```

**Why this matters:**
- The SDK handles JSON serialization automatically
- Using JSON.stringify() will double-serialize and break the request
- The server won't be able to parse the body

## Common Mistakes Checklist

Before implementing, verify you're NOT doing these:

**SDK Usage:**
- [ ] Using regular fetch() instead of the Medusa JS SDK (causes missing header errors)
- [ ] Not using existing SDK methods for built-in endpoints (e.g., using sdk.client.fetch("/store/products") instead of sdk.store.product.list())
- [ ] Using JSON.stringify() on the body parameter
- [ ] Manually setting Content-Type headers (SDK adds them)
- [ ] Hardcoding SDK import paths (locate in project first)
- [ ] Not using sdk.client.fetch() for custom routes

**React Query:**
- [ ] Not invalidating queries after mutations
- [ ] Using flat query keys instead of hierarchical
- [ ] Not handling loading and error states
- [ ] Forgetting to disable buttons during mutations (isPending)

**Data Display:**
- [ ] **CRITICAL**: Dividing prices by 100 when displaying (prices are stored as-is: $49.99 = 49.99, NOT in cents)

**Error Handling:**
- [ ] Not implementing onError callbacks
- [ ] Not showing error messages to users
- [ ] Not handling network failures gracefully

## How to Use

**For detailed patterns and examples, load reference file:**

```
references/frontend-integration.md - SDK usage, React Query patterns, API integration
```

The reference file contains:
- Step-by-step SDK integration patterns
- Complete React Query examples
- Correct vs incorrect code examples
- Query key best practices
- Optimistic update patterns
- Error handling strategies

## When to Use MedusaDocs MCP Server

**Use this skill for (PRIMARY SOURCE):**
- How to call custom API routes from storefront
- SDK usage patterns (sdk.client.fetch)
- React Query integration patterns
- Common mistakes and anti-patterns

**Use MedusaDocs MCP server for (SECONDARY SOURCE):**
- Built-in SDK methods (sdk.admin.*, sdk.store.*)
- Official Medusa SDK API reference
- Framework-specific configuration options

**Why skills come first:**
- Skills contain critical patterns like "don't use JSON.stringify" that MCP doesn't emphasize
- Skills show correct vs incorrect patterns; MCP shows what's possible
- Planning requires understanding patterns, not just API reference

## Integration with Backend

**⚠️ CRITICAL: ALWAYS use the Medusa JS SDK - NEVER use regular fetch()**

When building features that span backend and frontend:

1. **Backend (building-with-medusa skill):** Module → Workflow → API Route
2. **Storefront (this skill):** SDK → React Query → UI Components
3. **Connection:**
   - Built-in endpoints: Use existing SDK methods (`sdk.store.product.list()`)
   - Custom API routes: Use `sdk.client.fetch("/store/my-route")`
   - **NEVER use regular fetch()** - missing publishable API key causes errors

**Why the SDK is required:**
- Store routes need `x-publishable-api-key` header
- Admin routes need `Authorization` and session headers
- SDK handles all required headers automatically
- Regular fetch() without headers → authentication/authorization errors

See `building-with-medusa` for backend API route patterns.
