Set Up a Headless Storefront With Next.js in a Weekend

by Emma Rodriguez
Set Up a Headless Storefront With Next.js in a Weekend

You've probably heard the word "headless" thrown around in e-commerce circles and assumed it was something only well-funded startups with a dedicated engineering team could pull off. I thought the same thing when I was running my first store on a hosted platform, watching my page-speed scores tank every time I added another app to the stack.

Then I spent one weekend — genuinely, Saturday morning to Sunday evening — wiring up a Next.js front end to a commerce API, and I came out the other side with a storefront that scored 94 on Google PageSpeed, loaded product pages in under 800 ms on a 4G connection, and cost me $0 in platform fees to host on Vercel's free tier. That's the version of headless I want to walk you through today.

This isn't about chasing a trend. It's about giving you a practical path to a store you actually own, end to end.

What "Headless" Actually Means for a Small Store

In a traditional platform (think any all-in-one builder), the front end — the part your customers see — is bundled with the back end — the cart, the product catalog, the checkout logic. That's convenient, but it also means the platform controls your performance, your design limits, and often your monthly bill.

Headless just means you split those two things apart. Your back end lives somewhere (a commerce API, a simple database, even a spreadsheet-powered tool for very small catalogs), and your front end is a completely separate app you build however you like. Next.js is the front-end framework most small operators reach for because it handles server-side rendering, static generation, and image optimization out of the box — things that matter a lot for SEO and speed.

The trade-off is real: you're writing code, or at least reading it. But if you can follow a recipe, you can follow this tutorial.

The Stack You'll Actually Use

Before you touch a keyboard, let's agree on the pieces. You want tools with generous free tiers, good documentation, and no surprise invoices.

Front end: Next.js 14 (App Router). Free, open source, runs anywhere Node.js runs.

Commerce layer: Medusa.js — an open-source headless commerce engine you self-host. The community edition is free. A basic Digital Ocean droplet at $6/month handles up to roughly 10,000 monthly orders without breaking a sweat, based on their published benchmarks.

Hosting for the front end: Vercel. Their Hobby plan is free for personal and small-business projects with up to 100 GB of bandwidth per month.

Payments: Stripe. 2.9% + 30¢ per transaction, no monthly fee, and Medusa has a first-party Stripe plugin that takes about 20 minutes to configure.

Total recurring cost for a store doing under $5k/month in revenue: roughly $6–12/month depending on your droplet size. Compare that to the $79–299/month range you'd pay on a mid-tier hosted platform once you add the apps you actually need.

Saturday Morning: Get Your Back End Running

Start here because your front end needs real product data to render — otherwise you're just building against mock JSON forever.

Step 1 — Spin up a Medusa server. If you have Node 18+ installed, open your terminal and run:

npx create-medusa-app@latest my-store

The CLI will ask you a few questions (database preference, whether to seed demo data — say yes to the seed). It installs everything and starts a local server at http://localhost:9000. The admin dashboard lands at http://localhost:7001. Walk through it, add three or four real products with photos and prices. This takes about 45 minutes including coffee.

Step 2 — Confirm your Store API is responding. Hit http://localhost:9000/store/products in your browser. You should see a JSON response with your products. That URL is the endpoint your Next.js app will call. Screenshot it — you'll feel good looking back at this moment.

Step 3 — Deploy Medusa to your droplet. Digital Ocean's App Platform can deploy directly from a GitHub repo. Push your Medusa folder, connect the repo, set your environment variables (database URL, JWT secret, Stripe keys), and let it build. First deploy takes 8–12 minutes. Once it's live, swap localhost:9000 for your droplet URL everywhere going forward.

Saturday Afternoon: Build the Next.js Storefront

Now the fun part. In a new terminal folder:

npx create-next-app@latest my-storefront

Choose TypeScript if you're comfortable with it; plain JavaScript works fine too. Then install one helper library:

npm install @medusajs/medusa-js

This is Medusa's official JS client. It wraps all the fetch calls you'd otherwise write by hand.

Build your product listing page. In app/page.tsx (or app/page.js), fetch your products at build time using Next.js's generateStaticParams or a simple async server component:

import Medusa from "@medusajs/medusa-js"

const client = new Medusa({ baseUrl: process.env.NEXT_PUBLIC_MEDUSA_URL })

export default async function HomePage() {
  const { products } = await client.products.list()
  return (
    <main>
      {products.map((p) => (
        <div key={p.id}>
          <h2>{p.title}</h2>
          <p>${(p.variants[0].prices[0].amount / 100).toFixed(2)}</p>
        </div>
      ))}
    </main>
  )
}

Ugly? Yes. Working? Absolutely. Style it after you confirm data flows correctly — this is the mistake most first-timers make, spending hours on CSS before the data pipeline is solid.

Build a product detail page. Create app/products/[handle]/page.js. Use generateStaticParams to pre-render every product page at build time. That's how you hit those sub-800 ms load times — the HTML is already built before the customer ever clicks.

Wire up the cart. Medusa manages cart state server-side via a cart ID stored in a cookie. The flow is: create cart on first add-to-cart → store cart ID in a cookie → retrieve and update that cart on subsequent visits. Their documentation has a copy-paste cart hook that handles this in about 60 lines. Don't reinvent it.

Sunday: Checkout, Polish, and Launch

By Sunday morning you should have a working product listing, individual product pages, and a cart. Here's what Sunday is for.

Checkout with Stripe. Medusa's Stripe plugin creates a Payment Intent on their server and passes the client secret to your front end. You render Stripe's <PaymentElement /> component — which handles card validation, Apple Pay, and Google Pay automatically — and on success, Medusa marks the order complete. The Medusa docs have a step-by-step for this. Budget 2 hours including testing with Stripe's test card numbers.

Add a search bar (optional but worth it). Medusa's /store/products?q=keyword endpoint supports basic search out of the box. A simple controlled input that fires a fetch on change gives you functional search in under an hour. If you later want Algolia-quality results, Medusa has an Algolia plugin — but don't over-engineer on launch day.

Performance check. Before you point your domain, run your Vercel preview URL through Google PageSpeed Insights. Look at your Largest Contentful Paint (LCP) — you want it under 2.5 seconds. If it's slow, the usual culprit is an unoptimized hero image. Swap it to Next.js's <Image /> component, which handles lazy loading and WebP conversion automatically, and retest. Most stores I've seen go from a 68 to a 91 with that one change.

Connect your domain and go live. Vercel's domain settings take about 5 minutes. Add your custom domain, update your DNS records (they show you exactly what to add), and SSL is provisioned automatically. Your store is live.

A Real Example: How This Played Out for a Candle Brand

A friend of mine runs a small candle business — 47 SKUs, mostly direct-to-consumer. She was paying $189/month on a hosted platform plus $40/month in apps for reviews, bundles, and a loyalty widget. Total: $229/month, or $2,748/year.

We rebuilt her store on this exact stack over a long weekend. Her new monthly cost: $12 for a Medusa droplet, $0 for Vercel hosting. She kept Stripe (same rate she had before). Her PageSpeed score went from 61 to 89. Conversion rate in the first 30 days after launch was up 11% — she attributes most of that to faster load times on mobile, where 73% of her traffic comes from.

Is her stack more complex to maintain than a hosted platform? Slightly. But she runs updates once a month, has a developer friend she pays $50/hour for the occasional help, and she's saved over $2,500 in platform fees in the first year. That math works.

Three Things You Can Do Today

You don't have to do the whole weekend build right now. Here are three moves you can make today to get ready:

  1. Install Node.js 18+ and run npx create-medusa-app@latest locally. Just see it work. Seed the demo data, poke around the admin. It takes 20 minutes and removes 80% of the intimidation.

  2. Create a free Vercel account and connect a GitHub repo — even an empty Next.js starter. Watch a deploy happen. Understanding the deploy pipeline before you need it under pressure is genuinely calming.

  3. List your current platform's monthly cost, including every app. Write the number down. That number is your motivation. For a lot of store owners it's somewhere between $150 and $400/month — money that could go toward inventory, ads, or just staying profitable longer.

Headless commerce isn't magic, and this stack won't solve every problem you have. But if you've ever felt like your store's platform was a ceiling rather than a floor, this guide to building a weekend project might be the thing that changes that feeling. You've got the tools, you've got the steps, and the first one is just opening a terminal.

When you're ready to go deeper, look into Medusa's plugin ecosystem — there are community plugins for review pembeli, wishlists, and multi-currency that slot right into what you've already built.