dist/codex/medusa-commerce/skills/nextjs-patterns/SKILL.md
Build Next.js 15 storefronts for Medusa v2 — App Router conventions, server vs client components, Medusa JS SDK integration, data fetching, caching, and server actions. Use when building Medusa storefronts with Next.js.
npx skillsauth add orcaqubits/agentic-commerce-claude-plugins nextjs-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Fetch live docs:
site:docs.medusajs.com storefront nextjs for Medusa storefront guideshttps://docs.medusajs.com/resources/references/js-sdk for JS SDK method referencesite:nextjs.org docs app router for latest Next.js 15 App Router conventionssite:docs.medusajs.com nextjs starter storefront for starter template patternssite:docs.medusajs.com publishable api key storefront for API authentication setupsrc/app/
├── layout.tsx — Root layout (providers, SDK)
├── page.tsx — Home page
├── (main)/products/ — Product listing + [handle]/
├── (main)/cart/ — Cart, checkout, account
└── lib/medusa.ts — SDK client initialization
| Concept | File Convention | Purpose in Medusa Storefront |
|---------|----------------|------------------------------|
| Layout | layout.tsx | Wrap pages with providers, header, footer |
| Page | page.tsx | Route entry point (server component default) |
| Loading | loading.tsx | Streaming fallback UI |
| Error | error.tsx | Error boundary per route segment |
| Not Found | not-found.tsx | 404 page for invalid product handles, etc. |
| Route Groups | (group)/ | Organize without affecting URL |
| Dynamic Routes | [param]/ | Product handles, category slugs |
| Parallel Routes | @slot/ | Simultaneous layout regions |
| Criterion | Server Component | Client Component |
|-----------|-----------------|-----------------|
| Data fetching | Fetch from Medusa API directly | Use Tanstack Query hooks |
| SEO | Full HTML rendered on server | Not indexed by crawlers |
| Interactivity | No event handlers | onClick, onChange, etc. |
| State | No useState/useEffect | Full React hooks |
| Examples | Product listing, product detail, categories | Cart actions, quantity selector, search |
| Directive | None (default) | "use client" at top |
ProductPage (server)
├── ProductInfo (server) — Title, description, price
├── ProductImages (server) — Image gallery markup
├── AddToCart (client) — "use client", quantity, button
└── RelatedProducts (server) — Fetched server-side
| Option | Purpose | Example Value |
|--------|---------|---------------|
| baseUrl | Medusa server URL | http://localhost:9000 |
| publishableKey | Store API authentication | From admin dashboard |
| auth.type | Authentication strategy | "session" or "jwt" |
| Domain | Method Pattern | Component Type |
|--------|---------------|---------------|
| Products | sdk.store.product.list() | Server (listing) |
| Products | sdk.store.product.retrieve(id) | Server (detail) |
| Cart | sdk.store.cart.create() | Client (interaction) |
| Cart | sdk.store.cart.addLineItem() | Client (interaction) |
| Cart | sdk.store.cart.update() | Client (interaction) |
| Checkout | sdk.store.cart.addShippingMethod() | Client (checkout flow) |
| Customer | sdk.auth.login() | Client (auth form) |
| Customer | sdk.store.customer.retrieve() | Server or Client |
| Regions | sdk.store.region.list() | Server (layout) |
| Collections | sdk.store.collection.list() | Server (navigation) |
Call the Medusa SDK directly in server components — no hooks needed:
// Fetch live docs for server-side SDK
// usage and async component patterns
| Pattern | Use Case |
|---------|----------|
| Direct await sdk.store.* | Server components with async data |
| generateMetadata() | Dynamic SEO metadata from product data |
| generateStaticParams() | ISR/SSG for product and category pages |
| Hook / Concern | Purpose |
|----------------|---------|
| useQuery | Read data with caching and automatic refetch |
| useMutation | Write operations (add to cart, login) |
| useQueryClient | Invalidate cache after mutations |
| Query keys | Use consistent keys like ["cart", cartId] |
| Optimistic updates | Update cart UI immediately, rollback on error |
| Prefetching | Prefetch product data on hover for navigation |
| Layer | Scope | Medusa Use Case | |-------|-------|-----------------| | Request Memoization | Per-request dedup | Multiple components fetching same product | | Data Cache | Cross-request | Product catalog data (revalidate periodically) | | Full Route Cache | Entire page | Static product pages (ISR) | | Router Cache | Client-side | Navigation between cached pages |
| Strategy | Method | Use Case |
|----------|--------|----------|
| Time-based | revalidate: 60 (seconds) | Product listings, category pages |
| On-demand | revalidatePath() / revalidateTag() | After admin product update |
| No cache | cache: "no-store" | Cart, checkout, customer data |
| Data Type | Cache Strategy | Reasoning |
|-----------|---------------|-----------|
| Products | revalidate: 60-300 | Changes infrequently |
| Collections | revalidate: 300-3600 | Rarely changes |
| Cart | no-store | User-specific, changes constantly |
| Customer | no-store | Private, per-session |
| Regions | revalidate: 3600 | Almost never changes |
| Prices | revalidate: 60 | May change with promotions |
Server actions handle form submissions and mutations from server components:
// Fetch live docs for Next.js server actions
// with Medusa SDK mutation patterns
"use server"
| Use Case | Action Pattern |
|----------|---------------|
| Add to cart | Server action calling sdk.store.cart.addLineItem() |
| Update quantity | Server action with revalidatePath |
| Apply discount | Server action calling sdk.store.cart.update() |
| Customer login | Server action wrapping sdk.auth.login() |
Use server actions for simple form mutations. Use API routes when external systems need to call your storefront.
| Variable | Purpose | Where Used |
|----------|---------|-----------|
| NEXT_PUBLIC_MEDUSA_BACKEND_URL | Medusa server URL | Client + Server |
| NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY | Store API key | Client + Server |
| REVALIDATE_WINDOW | Default ISR revalidation time | Server only |
"use client" only for interactive elements; split pages into server (data) and client (interaction) sub-componentsloading.tsx for streaming with Suspense boundaries; prefetch data for likely navigation targets; optimize images with next/image and Medusa media URLsFetch the Medusa Next.js storefront documentation and JS SDK reference for exact method signatures, query key conventions, and starter template patterns before implementing.
development
Build with Spree's headless Next.js storefront — the official `spree/storefront` repo (Next.js 16 App Router with Server Actions and Turbopack, React 19 Server Components, Tailwind CSS 4, TypeScript 5, `@spree/sdk`, Sentry), server-only auth (httpOnly JWT cookies + publishable key), MeiliSearch faceted catalog, one-page checkout with Apple/Google Pay/Klarna/Affirm/SEPA, multi-region market routing, GA4 + JSON-LD SEO, and Vercel/Docker deployment. Use when forking or customizing the storefront, or evaluating headless adoption.
tools
Build Spree extensions as Rails engines — gem scaffolding, `bin/rails g spree:extension`, mounting routes/migrations/assets, the modern `prepend` decorator pattern (`*_decorator.rb` with `self.prepended(base)`), generators (`spree:model_decorator`, `spree:controller_decorator`), the four customization surfaces in preference order (Events > Webhooks > Dependencies > Decorators), Spree::Dependencies for swapping service objects, gem release/versioning, and the deprecated Deface engine. Use when building a reusable Spree extension or adding non-trivial customization to an app.
development
Build with Spree's event bus and Webhooks 2.0 — `Spree::Events` publication, `Spree::Subscriber` DSL with `subscribes_to` and `on`, wildcard matching, lifecycle events (`{model}.created/.updated/.deleted` via `publishes_lifecycle_events`), the canonical event catalog (order.*, payment.*, shipment.*, product.*), Webhooks 2.0 endpoints, HMAC-SHA256 signing (`X-Spree-Webhook-Signature`), exponential-backoff retries, and Sidekiq job orchestration. Use when wiring event-driven business logic, building webhook consumers, or replacing ActiveSupport callback chains.
tools
Cross-cutting Spree development patterns — the customization preference hierarchy (Events > Webhooks > Dependencies > Decorators), `Spree::Dependencies` service-object swapping, the `_decorator.rb` + `prepend` + `self.prepended` idiom, idempotent subscribers and webhook receivers, multi-store scoping discipline, prefixed IDs, calculator polymorphism (shipping/promotion/tax share the base), service-object composition with `dry-monads` or simple results, why to avoid `class_eval` reopening and Deface, and Spree-on-Rails idioms (Hotwire/Turbo Stimulus, ActiveStorage, Action Cable, Sidekiq). Use when designing the architecture of a Spree extension or solving cross-cutting concerns.