Medusa v2 Storefront with Next.js
Before writing code
Fetch live docs:
- Fetch
https://docs.medusajs.com/resources/storefront-development for storefront overview
- Web-search
site:docs.medusajs.com nextjs starter storefront for starter template
- Web-search
site:docs.medusajs.com JS SDK client setup for Medusa SDK configuration
- Web-search
site:docs.medusajs.com storefront API reference for Store API endpoints
- Web-search
site:github.com medusajs nextjs-starter-medusa for latest starter source
Storefront Architecture
Medusa v2 storefronts are headless -- they consume the Store API over HTTP:
| Layer |
Component |
| Frontend |
Next.js App (SSR/RSC) |
| HTTP Client |
Medusa JS SDK |
| Backend |
Medusa Backend (/store/* API routes) |
| Storage |
PostgreSQL / Redis |
Next.js Starter Structure
| Directory |
Key Files |
app/ |
layout.tsx, page.tsx (homepage) |
app/(main)/products/ |
page.tsx (listing), [handle]/page.tsx (detail) |
app/(main)/collections/ |
[handle]/page.tsx (collection) |
app/(main)/cart/, app/(main)/checkout/ |
Cart page, checkout flow |
app/(auth)/, app/account/ |
Login/register, customer dashboard |
lib/ |
sdk.ts (Medusa client), data/ (fetching), util/ |
components/ |
products/, cart/, checkout/, layout/ |
| Root |
next.config.js, .env.local |
JS SDK Client Setup
SDK Installation
The Medusa JS SDK provides typed access to the Store API:
// lib/sdk.ts — Fetch live docs for Medusa SDK initialization
import Medusa from "@medusajs/js-sdk"
export const sdk = new Medusa({
baseUrl: process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL!,
// Fetch live docs for publishableKey and other options
})
Environment Variables
NEXT_PUBLIC_MEDUSA_BACKEND_URL=http://localhost:9000
NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY=pk_...
The publishable API key is required for Store API requests and scopes them to a specific sales channel.
Key Store API Domains
| Domain |
SDK Namespace |
Key Operations |
| Products |
sdk.store.product |
List, retrieve by handle |
| Collections |
sdk.store.collection |
List, retrieve |
| Categories |
sdk.store.category |
List, tree |
| Cart |
sdk.store.cart |
Create, add items, update |
| Checkout |
sdk.store.cart |
Add shipping, complete |
| Customer |
sdk.store.customer |
Register, login, profile |
| Orders |
sdk.store.order |
List customer orders |
| Regions |
sdk.store.region |
List regions and currencies |
| Shipping |
sdk.store.fulfillment |
List shipping options |
Fetch live docs for the complete SDK namespace list and method signatures.
Server Components vs Client Components
| Pattern |
Use For |
| Server Component (RSC) |
Product listing, product detail, static content |
| Client Component |
Cart interactions, checkout form, quantity selectors |
| Server Action |
Cart mutations, checkout submission |
Data Fetching in Server Components
// app/(main)/products/[handle]/page.tsx
// Fetch live docs for SDK method signatures
export default async function ProductPage({ params }) {
const { product } = await sdk.store.product.retrieve(params.handle)
return <ProductTemplate product={product} />
}
Cart Flow
| Step |
Action |
SDK Method |
| 1. Create cart |
On first add-to-cart |
sdk.store.cart.create() |
| 2. Add line item |
User adds product |
sdk.store.cart.addLineItem() |
| 3. Update quantity |
User changes qty |
sdk.store.cart.updateLineItem() |
| 4. Remove item |
User removes product |
sdk.store.cart.removeLineItem() |
| 5. Set region |
Auto or user selection |
sdk.store.cart.update() |
Cart ID is typically stored in a cookie or localStorage for persistence across sessions.
Checkout Flow
| Step |
Required Data |
SDK Call |
| 1. Address |
Shipping + billing address |
sdk.store.cart.update() |
| 2. Shipping |
Selected shipping option |
sdk.store.cart.addShippingMethod() |
| 3. Payment |
Payment provider session |
sdk.store.cart.initiatePaymentSession() |
| 4. Complete |
Confirmation |
sdk.store.cart.complete() |
Customer Authentication
| Flow |
Description |
| Registration |
Create customer account via Store API |
| Login |
Authenticate and receive session token |
| Session |
Token stored in cookie, sent with requests |
| Profile |
Update customer info, addresses |
| Orders |
View past orders linked to customer |
// Fetch live docs for customer auth SDK methods
await sdk.auth.login("customer", "emailpass", {
email: "user@example.com",
password: "password",
})
Region and Currency Handling
- Medusa supports multiple regions with different currencies
- Storefront should detect or let the user select a region
- All product prices are region-aware
- Cart is tied to a region which determines currency and tax rules
Performance Considerations
| Technique |
Implementation |
| Static Generation (SSG) |
Use generateStaticParams for product pages |
| ISR |
Revalidate product pages periodically |
| Streaming |
Use Suspense boundaries for slow data |
| Image optimization |
Use Next.js <Image> with Medusa image URLs |
| Prefetching |
Use <Link> for product navigation |
Best Practices
- Use Server Components for data fetching -- minimize client-side JavaScript
- Store the cart ID in a cookie for cross-tab and SSR access
- Use the publishable API key to scope requests to the correct sales channel
- Handle region/currency at the layout level, not per-page
- Implement optimistic UI updates for cart operations to improve perceived performance
- Use
generateStaticParams for product and collection pages for SEO and speed
- Keep checkout as a linear flow -- do not allow skipping steps
Fetch the Medusa storefront documentation and Next.js starter source for exact SDK method signatures, component patterns, and checkout flow details before implementing.
1---2name: medusa-storefront3description: Build Medusa v2 storefronts with Next.js 15 — App Router, JS SDK client setup, Tanstack Query, server components, product pages, cart, and checkout flow. Use when developing headless storefronts.4---56# Medusa v2 Storefront with Next.js78## Before writing code910**Fetch live docs**:111. Fetch `https://docs.medusajs.com/resources/storefront-development` for storefront overview122. Web-search `site:docs.medusajs.com nextjs starter storefront` for starter template133. Web-search `site:docs.medusajs.com JS SDK client setup` for Medusa SDK configuration144. Web-search `site:docs.medusajs.com storefront API reference` for Store API endpoints155. Web-search `site:github.com medusajs nextjs-starter-medusa` for latest starter source1617## Storefront Architecture1819Medusa v2 storefronts are headless -- they consume the Store API over HTTP:2021| Layer | Component |22|-------|-----------|23| Frontend | Next.js App (SSR/RSC) |24| HTTP Client | Medusa JS SDK |25| Backend | Medusa Backend (`/store/*` API routes) |26| Storage | PostgreSQL / Redis |2728## Next.js Starter Structure2930| Directory | Key Files |31|-----------|-----------|32| `app/` | `layout.tsx`, `page.tsx` (homepage) |33| `app/(main)/products/` | `page.tsx` (listing), `[handle]/page.tsx` (detail) |34| `app/(main)/collections/` | `[handle]/page.tsx` (collection) |35| `app/(main)/cart/`, `app/(main)/checkout/` | Cart page, checkout flow |36| `app/(auth)/`, `app/account/` | Login/register, customer dashboard |37| `lib/` | `sdk.ts` (Medusa client), `data/` (fetching), `util/` |38| `components/` | `products/`, `cart/`, `checkout/`, `layout/` |39| Root | `next.config.js`, `.env.local` |4041## JS SDK Client Setup4243### SDK Installation4445The Medusa JS SDK provides typed access to the Store API:4647```typescript48// lib/sdk.ts — Fetch live docs for Medusa SDK initialization49import Medusa from "@medusajs/js-sdk"5051export const sdk = new Medusa({52 baseUrl: process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL!,53 // Fetch live docs for publishableKey and other options54})55```5657### Environment Variables5859```60NEXT_PUBLIC_MEDUSA_BACKEND_URL=http://localhost:900061NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY=pk_...62```6364The publishable API key is required for Store API requests and scopes them to a specific sales channel.6566## Key Store API Domains6768| Domain | SDK Namespace | Key Operations |69|--------|--------------|----------------|70| Products | `sdk.store.product` | List, retrieve by handle |71| Collections | `sdk.store.collection` | List, retrieve |72| Categories | `sdk.store.category` | List, tree |73| Cart | `sdk.store.cart` | Create, add items, update |74| Checkout | `sdk.store.cart` | Add shipping, complete |75| Customer | `sdk.store.customer` | Register, login, profile |76| Orders | `sdk.store.order` | List customer orders |77| Regions | `sdk.store.region` | List regions and currencies |78| Shipping | `sdk.store.fulfillment` | List shipping options |7980> **Fetch live docs** for the complete SDK namespace list and method signatures.8182## Server Components vs Client Components8384| Pattern | Use For |85|---------|---------|86| Server Component (RSC) | Product listing, product detail, static content |87| Client Component | Cart interactions, checkout form, quantity selectors |88| Server Action | Cart mutations, checkout submission |8990### Data Fetching in Server Components9192```typescript93// app/(main)/products/[handle]/page.tsx94// Fetch live docs for SDK method signatures95export default async function ProductPage({ params }) {96 const { product } = await sdk.store.product.retrieve(params.handle)97 return <ProductTemplate product={product} />98}99```100101## Cart Flow102103| Step | Action | SDK Method |104|------|--------|-----------|105| 1. Create cart | On first add-to-cart | `sdk.store.cart.create()` |106| 2. Add line item | User adds product | `sdk.store.cart.addLineItem()` |107| 3. Update quantity | User changes qty | `sdk.store.cart.updateLineItem()` |108| 4. Remove item | User removes product | `sdk.store.cart.removeLineItem()` |109| 5. Set region | Auto or user selection | `sdk.store.cart.update()` |110111Cart ID is typically stored in a cookie or localStorage for persistence across sessions.112113## Checkout Flow114115| Step | Required Data | SDK Call |116|------|--------------|---------|117| 1. Address | Shipping + billing address | `sdk.store.cart.update()` |118| 2. Shipping | Selected shipping option | `sdk.store.cart.addShippingMethod()` |119| 3. Payment | Payment provider session | `sdk.store.cart.initiatePaymentSession()` |120| 4. Complete | Confirmation | `sdk.store.cart.complete()` |121122## Customer Authentication123124| Flow | Description |125|------|-------------|126| Registration | Create customer account via Store API |127| Login | Authenticate and receive session token |128| Session | Token stored in cookie, sent with requests |129| Profile | Update customer info, addresses |130| Orders | View past orders linked to customer |131132```typescript133// Fetch live docs for customer auth SDK methods134await sdk.auth.login("customer", "emailpass", {135 email: "user@example.com",136 password: "password",137})138```139140## Region and Currency Handling141142- Medusa supports multiple regions with different currencies143- Storefront should detect or let the user select a region144- All product prices are region-aware145- Cart is tied to a region which determines currency and tax rules146147## Performance Considerations148149| Technique | Implementation |150|-----------|---------------|151| Static Generation (SSG) | Use `generateStaticParams` for product pages |152| ISR | Revalidate product pages periodically |153| Streaming | Use Suspense boundaries for slow data |154| Image optimization | Use Next.js `<Image>` with Medusa image URLs |155| Prefetching | Use `<Link>` for product navigation |156157## Best Practices158159- Use Server Components for data fetching -- minimize client-side JavaScript160- Store the cart ID in a cookie for cross-tab and SSR access161- Use the publishable API key to scope requests to the correct sales channel162- Handle region/currency at the layout level, not per-page163- Implement optimistic UI updates for cart operations to improve perceived performance164- Use `generateStaticParams` for product and collection pages for SEO and speed165- Keep checkout as a linear flow -- do not allow skipping steps166167Fetch the Medusa storefront documentation and Next.js starter source for exact SDK method signatures, component patterns, and checkout flow details before implementing.