You've been running your store on a hosted platform for a couple of years. Sales are fine, but every time you want to change something — a custom checkout step, a landing page that actually loads fast, a product quiz — you hit a wall. The theme editor can only stretch so far, and the app marketplace adds $30 here, $49 there until your monthly bill looks like a car payment.
Headless commerce keeps coming up in conversations, but every article you find seems written for a dev team with a Jira board and a six-month runway. You're a small operator. You have a weekend.
Good news: a basic headless storefront with Next.js is genuinely a two-day project if you know which decisions to make up front. I've done this twice — once for a skincare brand hitting about $340K/year and once for a hobby shop closer to $80K — and the pattern is repeatable. Let me walk you through it.
What "Headless" Actually Means (Plain English)
A traditional e-commerce setup bundles the storefront (what customers see) and the commerce logic (cart, checkout, inventory) into one system. Headless just means you split those two things apart. Your storefront lives in one place — in this case a Next.js app — and it talks to a commerce backend via an API to get product data, handle carts, and process orders.
The storefront becomes just a website that happens to sell things. You control every pixel, every page load, every redirect. The backend handles the boring-but-critical stuff: payments, stock levels, order management.
Why does that matter for a small store? Three reasons:
- Speed. Next.js pre-renders pages at build time. A statically generated product page routinely scores 95+ on Core Web Vitals, and Google's own data shows a 1-second delay in mobile load time can reduce conversions by up to 20%.
- Cost control. Once you own the frontend, you stop paying for features baked into a platform theme you only use halfway.
- Flexibility. Want a custom bundle builder? A subscription toggle? You write it once, exactly how you need it.
Pick Your Backend Before You Write a Line of Code
This is the decision most tutorials skip, and it's the one that bites people later. Your Next.js frontend is just a consumer of whatever API your backend exposes. Pick the wrong backend and you'll be fighting its data model for months.
For a small store (under ~500 SKUs, under $2M/year), you have a few practical options:
- Medusa.js — open-source, self-hostable, free at the core. Good if you're comfortable with a Node.js server and want zero platform fees. Hosting on Railway or Render runs about $7–$25/month.
- Swell — hosted, developer-friendly API, generous free tier up to $500/month in sales. Pricing scales as a percentage of revenue after that.
- Crystallize — strong if your catalog has complex variants or you're in subscriptions. Pricing starts around $0 for low-volume stores.
- Shopify (headless via Storefront API) — yes, you can use Shopify purely as a backend while building your own frontend. You keep Shopify's checkout (which converts well) and lose the theme constraints. Costs your normal Shopify plan plus a small Storefront API fee.
For this tutorial I'll use Medusa because it's free and self-contained, but the Next.js patterns are almost identical regardless of which backend you choose.
Day One: Backend Up and Running
On Saturday morning, your goal is a working Medusa server with at least a few products in it. This takes two to four hours, not eight.
Step 1: Spin up Medusa locally.
You need Node 18+ and a PostgreSQL database. If you don't have Postgres locally, the easiest path is a free Supabase project — takes about three minutes to create.
npx create-medusa-app@latest my-store
The CLI walks you through database connection. Once it finishes, you'll have an admin panel at localhost:9000/app. Log in, add a few products with prices and images. Don't overthink the catalog right now — five products is enough to build against.
Step 2: Enable the Store API.
Medusa exposes a public Store API at /store. Hit localhost:9000/store/products in your browser and you should see JSON. That's your frontend's data source.
Step 3: Deploy the backend.
Push your Medusa project to GitHub, then connect it to Railway (railway.app). Add your Postgres connection string as an environment variable. A basic Railway deployment runs about $5–$10/month. Your backend is now live at a real URL — something like https://my-store-production.up.railway.app.
That's day one. Backend is done. Grab lunch.
Day Two: Build the Next.js Storefront
Sunday is for the frontend. You're building three pages: a product listing, a single product page, and a cart. That's a complete shopping experience.
Step 1: Create the Next.js app.
npx create-next-app@latest storefront --typescript --app
Use the App Router (the default in Next.js 14+). It handles caching and server components in a way that's perfect for e-commerce — product pages render on the server and get cached at the edge automatically.
Step 2: Fetch products with a server component.
Create app/products/page.tsx. Because this is a server component, you fetch directly — no useEffect, no loading spinners:
async function getProducts() {
const res = await fetch(`${process.env.MEDUSA_URL}/store/products`, {
next: { revalidate: 60 },
});
return res.json();
}
export default async function ProductsPage() {
const { products } = await getProducts();
return (
<ul>
{products.map((p: any) => (
<li key={p.id}>{p.title}</li>
))}
</ul>
);
}
The revalidate: 60 tells Next.js to refresh the cache every 60 seconds. Your product page is now statically served but stays fresh.
Step 3: Build the single product page.
Create app/products/[handle]/page.tsx. Use generateStaticParams to pre-render every product at build time:
export async function generateStaticParams() {
const res = await fetch(`${process.env.MEDUSA_URL}/store/products`);
const { products } = await res.json();
return products.map((p: any) => ({ handle: p.handle }));
}
This means every product page is a static HTML file at deploy time. First byte in under 100ms, no cold starts, no server round trips.
Step 4: Wire up the cart.
Cart state needs to persist across pages, so this is the one place you'll use a client component and React context. Create a CartProvider that stores a Medusa cart ID in localStorage and exposes addItem / removeItem functions. Each function calls the Medusa Store API (POST /store/carts/:id/line-items).
This is the most involved part — plan for two to three hours here. If you want a shortcut, the open-source medusa-react package wraps all of this for you.
Step 5: Checkout.
For a first launch, don't build a custom checkout. Redirect to Medusa's hosted checkout or, if you went the Shopify-backend route, redirect to Shopify's native checkout. Custom checkout is a week-long project on its own. Ship first.
A Real Example: The Skincare Brand
When I helped rebuild the skincare store's frontend, the biggest win wasn't the design — it was load time. The old theme-based storefront had a Largest Contentful Paint of 4.1 seconds on mobile. After switching to a Next.js headless setup with the same Shopify backend (using the Storefront API), LCP dropped to 1.3 seconds. Over the next 90 days, mobile conversion rate climbed from 1.8% to 2.6%. On $340K/year in revenue, that's roughly $27K in additional sales without touching a single ad.
The total cost to run the new frontend: about $20/month on Vercel's pro plan. The old setup was paying $79/month for a theme license plus $120/month in apps that handled things Next.js does natively.
Three Things to Do Before You Go Live
Before you point your domain at the new storefront, check these off:
-
Set up redirects for any existing URLs. If your old store had
/collections/skincareand your new one has/products?category=skincare, add a 301 redirect innext.config.js. Broken links kill SEO rankings you've spent years building. -
Add an error boundary. Wrap your root layout in an
error.tsxfile so a failed API call shows a friendly message instead of a blank white page. Takes ten minutes, saves a lot of customer confusion. -
Test on a real phone, not just browser dev tools. Resize all you want on desktop — nothing replaces tapping through the cart flow on an actual iPhone or Android device. Pay attention to tap target sizes and whether the checkout redirect works on Safari (it usually does, but verify).
Do you have a staging environment set up? Even a free Vercel preview deployment tied to a staging branch is enough. Merge to main only after you've clicked through the full purchase flow on staging.
You Can Do This
Headless commerce used to mean a six-figure agency engagement. The tooling has genuinely caught up to small operators now. Next.js handles the hard frontend parts — caching, routing, image optimization — and open-source backends like Medusa handle the commerce logic. You're stitching two mature tools together, not inventing anything.
A weekend is a realistic timeline for a working prototype. A second weekend gets you to something launch-ready. And when you're live, you own the code — no platform can change a pricing tier or deprecate a theme feature and take your storefront down with it.
Your next step: pick your backend today (even just decide between Medusa and Shopify Storefront API), and spend 30 minutes on Saturday morning running the create-medusa-app CLI. You'll have products in an API before lunch, and that momentum carries the rest of the weekend.