Imoran ai LogoImoran ai Logo

Building a Headless Shopify Store with Next.js

How I built my merch store as a headless Shopify storefront: a custom Next.js frontend on the Storefront API, TanStack Query for the cart, GSAP and Lenis for motion, and SEO metadata plus a sitemap generated from Shopify product handles.

Published: July 1, 2026

Last updated: July 1, 2026

Building a Headless Shopify Store with Next.js

I wanted my merch store to feel like the rest of my site, not like a Shopify theme with my logo dropped on top. So I built it headless: Shopify runs the commerce, and a custom Next.js frontend runs everything you see. Here is how the pieces fit, and the parts that surprised me.

Why I skipped the Shopify theme

Shopify themes are fine. They stand up fast and handle a lot for you. But you are working inside Liquid and someone else's structure, and the moment I wanted GSAP transitions and my own design system, I was fighting the theme instead of building.

Going headless meant I owned the frontend completely and let Shopify keep doing the part it is genuinely good at: products, inventory, payments, and checkout.

What "headless" actually means here

Headless just means the storefront and the commerce backend are two separate things talking over an API. Shopify holds the products, collections, prices, and orders. My Next.js app asks for that data through Shopify's Storefront API and renders it however I want. Nothing about my UI is Shopify's concern, and nothing about inventory or payments is mine.

The Storefront API

The Storefront API is a GraphQL API built exactly for this. You create a storefront access token in Shopify and query it from your frontend for products, collections, and carts. It is read-oriented for the catalog and gives you cart mutations for the buying flow.

A products query looks about like this:

const QUERY = `
  query Products($first: Int!) {
    products(first: $first) {
      nodes {
        id
        title
        handle
        featuredImage { url altText }
        priceRange { minVariantPrice { amount currencyCode } }
      }
    }
  }
`

const res = await fetch(`https://${shop}/api/2024-10/graphql.json`, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Shopify-Storefront-Access-Token": token,
  },
  body: JSON.stringify({ query: QUERY, variables: { first: 20 } }),
})

The `handle` is the field that matters most. It is Shopify's URL slug for a product, and it becomes the thing my routes, my metadata, and my sitemap all hang off.

Fetching products with TanStack Query

I used TanStack Query for the client-side data. Product lists, a single product, the cart, all of it goes through queries and mutations with a cache in front. A product I already loaded does not refetch when I navigate back to it, and cart updates feel instant because the UI can update optimistically while the mutation runs.

I could have done more of this on the server, and for the catalog pages I did use server rendering so the product content is there for search engines. But the cart is client state that changes as you click around, so TanStack Query earned its place there.

The cart, and why checkout stays on Shopify

This is the part people get wrong when they first go headless. You build the cart, but you do not build the checkout.

Shopify's cart is an object you create and add lines to through the Storefront API. `cartCreate` gives you a cart, `cartLinesAdd` puts items in it, and the cart comes back with a `checkoutUrl`. When the customer is ready to pay, you send them to that URL and Shopify's hosted checkout takes over.

// build the cart yourself, then hand off to Shopify to take the money
window.location.href = cart.checkoutUrl

I like this boundary. Payments, taxes, shipping rates, fraud checks, and PCI compliance are Shopify's problem, not mine. I get to obsess over the storefront and leave the regulated part with the people who do it for a living.

Motion: GSAP and Lenis

The reason to go headless was the frontend, so I spent real time on how it moves. GSAP handles clip-path reveals and staggered product lists, so items arrive with intent instead of popping in. Lenis handles smooth scrolling, which sounds like a small thing until you scroll a store that doesn't have it. For a small merch shop, that polish is most of what makes it feel like a real brand instead of a template.

None of it touches the commerce layer. It is all frontend, which is the whole point of keeping the two apart.

SEO: metadata and a sitemap from product handles

If people can't find the products, the store doesn't sell, so I couldn't treat SEO as a last step.

I built a metadata system in Next.js that generates a title and description for every product and every collection straight from the Shopify data, so each page tells search engines what it actually is. Then I generated the sitemap and `robots.txt` automatically by fetching every product handle from Shopify at build time. Add a product in Shopify, rebuild, and it is in the sitemap without me touching anything. The `handle` from earlier is what ties the route, the metadata, and the sitemap entry together.

What I'd tell you before you start

Two things.

Decide what is yours and what is Shopify's early. The clean line is that you own presentation and Shopify owns money. Every time I was tempted to reimplement something Shopify already does, like checkout or tax or shipping, stopping myself was the right call.

And treat the `handle` as your backbone. Routes, metadata, sitemap, and links all key off it, so lean on it as the stable identifier instead of reaching for the numeric ID.

Where it leaves me

Headless Shopify gave me a store that looks and moves like the rest of my work, with Shopify quietly handling the parts I have no business rebuilding. If you want a shop that reads as your brand instead of a stock theme, this is how I'd build it for you.

Frequently asked questions

What is a headless Shopify store?

Headless means the storefront and Shopify are separate. Shopify holds the products, inventory, and payments, while a custom frontend (mine is Next.js) renders the shop and talks to Shopify over the Storefront API. You own the design, Shopify owns commerce.

Do I still use Shopify checkout when the store is headless?

Yes, and you should. You build the cart with the Storefront API, but the cart returns a checkoutUrl and you send the customer there. Shopify's hosted checkout handles payments, taxes, shipping, and PCI compliance so you don't have to.

Why the Storefront API and not the Admin API?

The Storefront API is built for customer-facing shops and is safe to call from the frontend with a storefront access token. The Admin API manages the store (creating products, reading orders) and holds far more power, so it stays on the server.

Can you build a headless Shopify store for me?

Yes. If you want a shop that looks like your brand instead of a stock theme, with Shopify still handling checkout, this is what I build. Tell me what you're selling and I will map out an approach.

Let's Work
Together

Hand Reaching

Have a project in mind? We'd love to hear about it. Let's create something great together!

PRODUCT DEVELOPERUI ENGINEERMVP DEVELOPMENTFRONTEND DEVELOPMENTPRODUCT DEVELOPERUI ENGINEERMVP DEVELOPMENTFRONTEND DEVELOPMENTPRODUCT DEVELOPERUI ENGINEERMVP DEVELOPMENTFRONTEND DEVELOPMENT