Next.js 15 Storefront Patterns for Medusa v2
Before writing code
Fetch live docs:
- Web-search
site:docs.medusajs.com storefront nextjsfor Medusa storefront guides - Fetch
https://docs.medusajs.com/resources/references/js-sdkfor JS SDK method reference - Web-search
site:nextjs.org docs app routerfor latest Next.js 15 App Router conventions - Web-search
site:docs.medusajs.com nextjs starter storefrontfor starter template patterns - Web-search
site:docs.medusajs.com publishable api key storefrontfor API authentication setup
App Router Conventions
Directory Structure
src/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
Key App Router Concepts
| 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 |
Server vs Client Components
Decision Matrix
| 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 |
Component Split Pattern
ProductPage (server)
├── ProductInfo (server) — Title, description, price
├── ProductImages (server) — Image gallery markup
├── AddToCart (client) — "use client", quantity, button
└── RelatedProducts (server) — Fetched server-side
Medusa JS SDK Integration
SDK Initialization and Configuration
| 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" |
Common SDK Methods
| 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) |
Data Fetching Patterns
Server Component Fetching
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 |
Client Component Fetching (Tanstack Query)
| 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 |
Caching Strategies
Next.js Caching Layers
| 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 |
Revalidation Patterns
| 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 |
Cache Configuration per Data Type
| 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
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.
Storefront Environment Variables
| 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 |
Best Practices
- Component boundaries — default to server components; add
"use client"only for interactive elements; split pages into server (data) and client (interaction) sub-components - SDK usage — initialize once in a shared module; use server-side calls in server components for SEO; use Tanstack Query in client components for reactivity and caching
- Caching discipline — cache product and collection data aggressively; never cache cart or customer data; use on-demand revalidation for admin-triggered updates; monitor cache hit rates
- Performance — use
loading.tsxfor streaming with Suspense boundaries; prefetch data for likely navigation targets; optimize images withnext/imageand Medusa media URLs
Fetch the Medusa Next.js storefront documentation and JS SDK reference for exact method signatures, query key conventions, and starter template patterns before implementing.