Why Go Headless with Shopify?
Shopify's default Liquid themes are excellent for most stores. But as brands grow, they hit limitations: slow page transitions, rigid layout constraints, inability to share components across channels. Going headless solves this: Shopify becomes the commerce backend (inventory, orders, payments), and Next.js becomes the frontend you fully control.
The result: 90+ Core Web Vitals, instant page transitions, and a React component library you own completely.
Architecture Decision: Hydrogen 2.0 vs Custom Next.js
This is the decision the rest of the build inherits, and it is worth taking slowly. The wider case for Next.js as a commerce storefront, and what it costs to own afterwards, sits alongside this. The framework choice matters less than the rendering and caching model behind it, which is the part of headless architecture that decides whether the storefront ends up faster than the theme it replaced.
Hydrogen 2.0 (Remix-based)
- Shopify's official headless framework: built on Remix
- Best-in-class for Shopify-specific patterns (cart, checkout, metafields)
- Deploys to Oxygen (Shopify's edge hosting): free with Shopify Plus
- Choose if: you're going fully Shopify-native and want the fastest time-to-launch
Next.js + Storefront API
- More flexibility: mix Shopify data with other APIs (CMS, ERP, reviews)
- Larger ecosystem: React component libraries, Vercel deployment, edge caching
- Choose if: you need a multi-source data architecture or prefer the React/Next.js ecosystem
This guide covers the Next.js 14 + Storefront API path.
Project Setup
npx create-next-app@latest my-shopify-store --typescript --tailwind --app
cd my-shopify-store
npm install @shopify/storefront-api-client graphql
Storefront API Client
// lib/shopify.ts
import { createStorefrontApiClient } from '@shopify/storefront-api-client';
export const shopify = createStorefrontApiClient({
storeDomain: process.env.SHOPIFY_STORE_DOMAIN!,
apiVersion: '2025-01',
publicAccessToken: process.env.SHOPIFY_STOREFRONT_TOKEN!,
});
Product Pages with ISR
Use Incremental Static Regeneration (ISR) for product pages: they are statically generated at build time but revalidated in the background when products change. Moving templates into this tier one at a time is also the safest way to run a migration from an existing theme, because each move is small enough to measure and reverse:
// app/products/[handle]/page.tsx
export const revalidate = 3600; // revalidate every hour
export async function generateStaticParams() {
const { data } = await shopify.request(GET_ALL_PRODUCT_HANDLES);
return data.products.edges.map(({ node }) => ({ handle: node.handle }));
}
export default async function ProductPage({ params }) {
const { data } = await shopify.request(GET_PRODUCT, { variables: { handle: params.handle } });
return <ProductView product={data.product} />;
}
Cart Architecture
Use Shopify's Cart API with a React Context for global cart state:
- Create cart on first item add:
cartCreatemutation - Store
cartIdin a cookie, not localStorage, so it works on server components - Use optimistic updates for instant UI response on add/remove
- Cart drawer as a Server Component: renders cart items SSR for performance
Checkout Redirect
Shopify headless checkout uses a redirect: the cart's checkoutUrl sends users to Shopify's hosted checkout. This is intentional: Shopify's checkout handles PCI compliance, payment processing, and fraud detection.
With Shopify Plus, you can use Checkout Extensions to customize the checkout UI (add custom fields, upsells, payment method ordering) while keeping it on Shopify's infrastructure.
SEO Best Practices
- Use Next.js
generateMetadata(): fetch product title, description, and OG image server-side - Canonical URLs: always point to the primary domain, not
*.myshopify.com - Structured data: output
Product+OfferJSON-LD in the page head - XML sitemap: generate dynamically from Storefront API product/collection lists
- hreflang: if multi-market, output correct language/region tags per page
Core Web Vitals Targets
- LCP < 2.5s: preload hero image, serve from Shopify CDN with width params
- CLS = 0: reserve image dimensions with aspect-ratio CSS, avoid layout shifts from font loading
- INP < 200ms: keep client-side JS lean; defer analytics scripts
Deployment: Vercel
Next.js + Vercel is the natural production stack. Key config:
- Enable Edge Runtime for product and collection pages: served from 100+ global PoPs
- Use Vercel's Image Optimization:
next/imageserves WebP with correct sizing automatically - Environment variables: store Shopify tokens in Vercel project settings, not in code
Building a headless Shopify storefront? I've delivered 5+ production builds with 90+ CWV scores. Let's talk →



