Medusa v2 Storefront with Next.js
Before writing code
Fetch live docs:
- Fetch
https://docs.medusajs.com/resources/storefront-developmentfor storefront overview - Web-search
site:docs.medusajs.com nextjs starter storefrontfor starter template - Web-search
site:docs.medusajs.com JS SDK client setupfor Medusa SDK configuration - Web-search
site:docs.medusajs.com storefront API referencefor Store API endpoints - Web-search
site:github.com medusajs nextjs-starter-medusafor 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
generateStaticParamsfor 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.