DTC Brand Migrated Shopify to Headless Next.js: 249% ROAS Increase
In March 2024, a DTC skincare brand came to us with a problem that's painfully common: their Shopify theme was slow, their ad spend was hemorrhaging money, and their Core Web Vitals were so deep in the red that Google was essentially deprioritizing them in search. Eight months later, their return on ad spend had increased 249%, their LCP dropped from 8.2s to 1.4s, and every single Core Web Vital metric was solidly green. This is the full story of how we got there — the architecture decisions, the tradeoffs, the mistakes, and the actual numbers.
Table of Contents
- The Starting Point: A Shopify Store in Trouble
- Why Headless and Why Now
- Choosing the Stack: Next.js, Hydrogen, and the Storefront API
- The Migration Architecture
- Fixing Core Web Vitals: What Actually Moved the Needle
- The ROAS Story: How Performance Became Profit
- Timeline, Budget, and Real Costs
- Lessons Learned and What We'd Do Differently
- FAQ

The Starting Point: A Shopify Store in Trouble
Let's call them GlowCo (NDA, you know the drill). They're a DTC skincare brand doing about $3.2M annually through their Shopify Plus store. They had 47 SKUs, a subscription model through Recharge, and they were spending roughly $85K/month on Meta and Google Ads.
The problem wasn't their product or their marketing team. The problem was their website.
Here's what their metrics looked like when they reached out to us:
| Metric | Value (March 2024) | Status |
|---|---|---|
| Largest Contentful Paint (LCP) | 8.2s | 🔴 Poor |
| First Input Delay (FID) | 340ms | 🔴 Poor |
| Cumulative Layout Shift (CLS) | 0.42 | 🔴 Poor |
| Interaction to Next Paint (INP) | 510ms | 🔴 Poor |
| Mobile PageSpeed Score | 18/100 | 🔴 |
| Desktop PageSpeed Score | 42/100 | 🟡 |
| Bounce Rate (Mobile) | 71% | — |
| Conversion Rate | 1.2% | — |
| Meta Ads ROAS | 1.8x | — |
| Google Ads ROAS | 2.1x | — |
A PageSpeed score of 18 on mobile. I've seen bad Shopify stores, but this one was carrying a theme with 2.4MB of unoptimized JavaScript, 14 render-blocking third-party scripts (Klaviyo, Yotpo, a loyalty app, two different popup tools, and a handful of analytics scripts), and hero images that were 3MB PNGs being served without any responsive sizing.
Their Shopify theme was a heavily customized version of a popular premium theme, modified over three years by at least four different freelancers. The Liquid template code was a nested mess of conditional logic that nobody fully understood.
But here's what really caught my attention: their marketing team showed us that their Meta Ads quality scores had been declining for months. Meta's algorithm penalizes landing pages that load slowly. Google Shopping was the same story — their ads were getting fewer impressions at higher CPCs because the landing page experience score was tanking.
They weren't just losing organic traffic. They were literally paying more for every click because their site was slow.
Why Headless and Why Now
GlowCo had already explored the obvious options. They'd tried optimizing their existing Shopify theme — removed some apps, compressed images, switched to a slightly lighter theme. It helped, barely. LCP went from 8.2s to about 6.8s. Still deep red.
The fundamental issue is one we see repeatedly with Shopify's monolithic architecture: you don't control the rendering pipeline. Shopify's servers render Liquid templates, inject their own JavaScript (Shopify's core JS alone is ~200KB), and you're at the mercy of whatever the theme and apps are doing.
Going headless meant decoupling the frontend completely from Shopify's rendering layer. Shopify would still handle the commerce backend — products, inventory, checkout, payments — but we'd build the entire customer-facing experience from scratch.
The timing made sense for three reasons:
Shopify's Storefront API had matured significantly. By early 2024, the Storefront API covered almost everything you'd need for a full store experience, including cart management, customer accounts, and metafield access.
Shopify Checkout Extensibility was now available on Plus. This meant we didn't need to rebuild checkout (which, honestly, is where headless used to fall apart).
The business case was clear. If improving site speed could reduce their CPC by even 15-20% while improving conversion rates, the project would pay for itself within 3-4 months.
Choosing the Stack: Next.js, Hydrogen, and the Storefront API
This is where things get interesting, because we had a genuine debate about the stack.
The Contenders
Shopify's official answer for headless is Hydrogen — their React-based framework built on Remix. It's tightly integrated with Shopify's APIs and deployed on Oxygen (Shopify's hosting platform).
But we ultimately went with Next.js 14 (App Router) using Shopify's Hydrogen React library — not the full Hydrogen/Remix framework.
Here's why:
| Factor | Hydrogen (Remix/Oxygen) | Next.js + Hydrogen React | Astro + Storefront API |
|---|---|---|---|
| SSR/SSG flexibility | Good (Remix streaming) | Excellent (ISR, SSG, SSR, RSC) | Excellent (Islands, SSG) |
| React ecosystem | Full | Full | Partial (Islands) |
| Hosting options | Oxygen only (or self-host) | Vercel, Netlify, AWS, self-host | Any static host + SSR |
| Shopify integration depth | Native | Via @shopify/hydrogen-react | Manual API calls |
| Team familiarity | Low | High | Medium |
| Edge rendering | Oxygen Workers | Vercel Edge, Cloudflare | Cloudflare, Netlify |
| Community/ecosystem | Growing | Massive | Growing fast |
We considered Astro seriously. For a content-heavy site with less interactivity, Astro's partial hydration model would've been ideal. But GlowCo's site needed heavy client-side interactivity — a product quiz, a skincare routine builder, quick-add cart drawers, real-time subscription management. Next.js's React Server Components gave us the best balance of server-rendered performance with client-side richness.
We also chose to deploy on Vercel rather than Oxygen. Vercel's edge network and ISR (Incremental Static Regeneration) capabilities gave us fine-grained caching control that we couldn't easily replicate on Oxygen at the time.
Our final stack:
- Next.js 14 (App Router with React Server Components)
- @shopify/hydrogen-react for cart, Storefront API utilities
- Shopify Storefront API (GraphQL) for product data
- Shopify Plus Checkout (native, not custom-built)
- Sanity CMS for editorial content, landing pages, and blog posts
- Vercel for hosting and edge functions
- Recharge via their headless API for subscriptions
- Klaviyo loaded asynchronously via a lightweight custom integration
If you're evaluating a similar setup, our team at Social Animal has deep experience with this exact architecture — we've documented our approach to headless CMS development and Next.js development if you want the broader picture.

The Migration Architecture
Data Layer
We kept Shopify as the source of truth for all commerce data. Products, variants, inventory, pricing, customers, orders — all stay in Shopify. This was non-negotiable. Shopify's commerce engine is battle-tested and their checkout conversion rates are hard to beat.
For content, we set up Sanity as a headless CMS. Product pages pulled structured data from Shopify (pricing, variants, inventory) and editorial content from Sanity (ingredient stories, usage guides, cross-sell narratives). This separation is crucial — it means the marketing team can update page content without touching product data, and the ops team can manage inventory without breaking landing pages.
// Simplified product page data fetching (Next.js App Router)
async function getProductPageData(handle: string) {
const [shopifyProduct, sanityContent] = await Promise.all([
getShopifyProduct(handle), // Storefront API GraphQL
getSanityProductContent(handle) // Sanity GROQ query
]);
return {
product: shopifyProduct,
editorial: sanityContent,
// Merge metafields for structured data
structuredData: buildProductSchema(shopifyProduct, sanityContent),
};
}
Rendering Strategy
Not every page needs the same rendering approach. We were deliberate about this:
- Product pages: ISR with 60-second revalidation. Products don't change every second, but we needed inventory accuracy within a minute.
- Collection pages: ISR with 5-minute revalidation. These change even less frequently.
- Homepage and landing pages: ISR with on-demand revalidation triggered by Sanity webhooks. When the marketing team publishes, a webhook hits our revalidation endpoint.
- Cart/account pages: Full client-side with server-rendered shells. These are inherently dynamic.
- Blog/editorial: Static generation at build time with on-demand revalidation.
The Cart Implementation
This is where @shopify/hydrogen-react earned its keep. The CartProvider and associated hooks handle all the cart state management, including optimistic UI updates. The cart lives in Shopify's Storefront API (not local state), which means it persists across sessions and devices.
// Cart drawer with optimistic updates
'use client';
import { CartProvider, useCart } from '@shopify/hydrogen-react';
function CartDrawer() {
const { lines, totalQuantity, cost, linesUpdate } = useCart();
return (
<Sheet>
{lines.map((line) => (
<CartLine
key={line.id}
line={line}
onQuantityChange={(quantity) =>
linesUpdate([{ id: line.id, quantity }])
}
/>
))}
<CartTotal cost={cost} />
<CheckoutButton />
</Sheet>
);
}
Checkout
We did not build a custom checkout. This is important. Shopify's native checkout (especially on Plus with Checkout Extensibility) has years of A/B testing and optimization baked in. Shop Pay alone can increase checkout conversion by 50% for returning customers. We customized it using Checkout UI Extensions for branding consistency and upsell widgets, but the core flow stays Shopify-native.
Fixing Core Web Vitals: What Actually Moved the Needle
Here's the part most case studies gloss over. Going headless doesn't magically fix your Core Web Vitals. You can absolutely build a slow Next.js site. I've seen it happen. What matters is the specific optimizations you make once you have control over the rendering pipeline.
LCP: From 8.2s to 1.4s
The single biggest LCP improvement came from three things:
Eliminating render-blocking resources. In the old Shopify theme, 14 third-party scripts loaded synchronously. In our Next.js build, critical CSS is inlined, JavaScript is code-split and loaded only where needed, and third-party scripts load after the main content is painted using
next/scriptwithstrategy="lazyOnload".Image optimization with
next/image. We served responsive images in WebP/AVIF format, properly sized for each viewport. Hero images went from 3MB PNGs to ~80KB AVIF files. The LCP element (usually the hero image) now loads as a priority prefetched resource.Edge caching with stale-while-revalidate. ISR on Vercel means the first visitor after a revalidation window gets a cached page instantly while the server regenerates in the background. Most visitors never wait for a server render.
CLS: From 0.42 to 0.02
Layout shift was caused by images without dimensions, fonts loading late (FOUT), and dynamically injected app widgets. Our fixes:
- All images have explicit
widthandheightattributes (or useaspect-ratioCSS) - Fonts preloaded with
font-display: swapand size-adjusted fallbacks - No dynamically injected third-party widgets above the fold
- Skeleton UI components for any async content
INP: From 510ms to 78ms
Interaction to Next Paint was the hardest to fix. The old theme had event handlers attached to the entire DOM, jQuery waterfalls, and heavy client-side JavaScript blocking the main thread.
React Server Components were the key here. By rendering most of the page on the server and only hydrating interactive components (cart drawer, product selectors, quiz widget), we dramatically reduced the amount of client-side JavaScript. Our total client bundle for the product page dropped from 2.4MB to 187KB.
The After Numbers
| Metric | Before (March 2024) | After (November 2024) | Status |
|---|---|---|---|
| LCP | 8.2s | 1.4s | 🟢 Good |
| FID | 340ms | 12ms | 🟢 Good |
| CLS | 0.42 | 0.02 | 🟢 Good |
| INP | 510ms | 78ms | 🟢 Good |
| Mobile PageSpeed | 18 | 94 | 🟢 |
| Desktop PageSpeed | 42 | 99 | 🟢 |
| Total JS (Product Page) | 2.4MB | 187KB | — |
| TTFB (p75) | 1.8s | 0.12s | — |
The ROAS Story: How Performance Became Profit
Here's where the rubber meets the road. Nobody migrates to headless for fun — the business case has to be there.
The new site launched in phases between July and October 2024. By November, we had clean data to compare. The results were, frankly, better than we projected.
Direct Conversion Impact
Mobile bounce rate dropped from 71% to 38%. That alone is massive — it means 46% more visitors were sticking around to even see the product. Mobile conversion rate went from 1.2% to 2.8%.
Desktop conversion rate improved from 2.4% to 3.9%.
Overall blended conversion rate: 1.2% → 3.1%
Ad Platform Impact
This is the part that surprised even us. Within 6 weeks of the Core Web Vitals passing green across the board:
- Google Ads CPC dropped 22% — Google's landing page experience score improved, directly lowering the cost per click
- Meta Ads CPM decreased 18% — Meta's algorithm started showing their ads more (better landing page = higher relevance score = lower costs)
- Google Shopping impression share increased 34% — Better page experience meant Google was more willing to show their product listings
The combined effect on ROAS:
| Channel | ROAS Before | ROAS After | Change |
|---|---|---|---|
| Meta Ads | 1.8x | 5.2x | +189% |
| Google Search Ads | 2.1x | 7.4x | +252% |
| Google Shopping | 2.4x | 8.8x | +267% |
| Blended | 1.95x | 6.8x | +249% |
The 249% blended ROAS increase came from two compounding factors: lower cost per click AND higher conversion rate. When your clicks cost less and each click converts at a higher rate, the math compounds beautifully.
Organic Traffic
I'd be remiss not to mention the SEO impact. Within 4 months of all Core Web Vitals going green:
- Organic traffic increased 67%
- Average position for target keywords improved by 4.2 positions
- Organic revenue increased 89%
Google has been clear that page experience is a ranking signal. This was real-world proof.
Timeline, Budget, and Real Costs
I believe in transparency about what projects like this actually cost. Here's the real breakdown:
Timeline: 7 months from kickoff to full launch (with a phased rollout starting month 5)
Team: 2 senior frontend developers, 1 Shopify backend specialist, 1 designer, 1 project manager (part-time)
Total project cost: ~$145,000
Monthly hosting/infrastructure: ~$350/month (Vercel Pro + Sanity Growth plan)
Ongoing Shopify Plus: $2,300/month (unchanged — they were already on Plus)
Payback period: The project paid for itself in 2.8 months based on the increased revenue from improved ROAS and conversion rates.
Is this kind of investment right for every brand? No. If you're doing under $1M annually, the math probably doesn't work yet. But for DTC brands spending $50K+ monthly on ads with poor Core Web Vitals, the ROI is almost always there. We're happy to talk specifics — reach out to us or check our pricing models for headless commerce projects.
Lessons Learned and What We'd Do Differently
What Worked
- Keeping Shopify checkout native was 100% the right call. No checkout conversion regression.
- ISR with on-demand revalidation gave us the best of both worlds: static performance with dynamic content.
- Phased rollout (launching blog/editorial pages first, then collections, then PDPs, then homepage) let us validate performance in production before migrating high-traffic pages.
What We'd Do Differently
- Start the Recharge headless migration earlier. Their headless API had some quirks we didn't anticipate, and it ate 3 weeks of our timeline. If you're using Recharge, budget extra time.
- Set up A/B testing infrastructure from day one. We added it in month 2 and lost some early comparison data.
- Use Vercel's Edge Config for feature flags instead of the environment variable approach we started with. Would've made the phased rollout cleaner.
One Honest Caveat
The headless approach adds operational complexity. GlowCo now manages two systems instead of one. Their marketing team can't just install a Shopify app and have it appear on the storefront — any new third-party integration needs development work. For GlowCo, at their scale and ad spend, the performance gains far outweigh this friction. But it's a real tradeoff you need to understand going in.
FAQ
How long does it take to migrate a Shopify store to headless Next.js? For a typical DTC brand with 30-100 SKUs, expect 4-8 months depending on complexity. GlowCo's project took 7 months due to custom features like their skincare quiz and Recharge subscription integration. Simpler stores with fewer custom features can be done in 4-5 months.
Does going headless break Shopify apps? Yes, most theme-dependent Shopify apps won't work in a headless setup. Apps that inject UI into your storefront (review widgets, loyalty popups, upsell tools) need to be replaced with either API-based alternatives or custom-built components. Backend apps (inventory management, shipping, etc.) continue to work fine since they don't touch the frontend.
Is Hydrogen or Next.js better for headless Shopify? It depends on your team and requirements. Hydrogen (built on Remix) offers tighter Shopify integration out of the box and is Shopify's officially supported path. Next.js offers a larger ecosystem, more hosting flexibility, and React Server Components. We chose Next.js for GlowCo because of the team's existing expertise and Vercel's edge caching capabilities. Both are excellent choices.
How much does a headless Shopify migration cost in 2025? Realistic budgets range from $80,000 to $250,000+ depending on store complexity, custom features, and agency rates. GlowCo's project was $145,000. Be wary of agencies quoting under $50K for a full headless build — you'll likely get a template with limited customization. Monthly infrastructure costs typically run $200-600 for hosting and CMS.
Do Core Web Vitals really affect Google Ads costs? Yes. Google Ads uses a "Landing Page Experience" score as part of its Quality Score calculation. Better page speed and Core Web Vitals scores lead to higher Quality Scores, which directly reduce your cost-per-click. GlowCo saw a 22% CPC reduction after their Core Web Vitals improved. Meta uses similar signals for ad relevance scoring.
Can you keep Shopify checkout when going headless? Absolutely, and we strongly recommend it. Shopify's checkout is highly optimized and includes features like Shop Pay (which can boost checkout conversion 50%+ for returning shoppers). With Shopify Plus, you can use Checkout Extensibility to customize the look and add upsells while keeping the core checkout flow intact.
What's the difference between headless Shopify and Shopify Hydrogen? Headless Shopify is a broad concept — any custom frontend that uses Shopify's Storefront API. Hydrogen is Shopify's specific framework for building headless storefronts, built on Remix and deployed on Shopify's Oxygen hosting. You can go headless with Next.js, Astro, Nuxt, or any framework. Hydrogen is just one option within the headless Shopify ecosystem.
Is headless worth it for small Shopify stores? Usually not. If you're doing under $1M in annual revenue and spending less than $20K monthly on ads, the cost of a headless migration likely won't produce a meaningful ROI. Focus on optimizing your existing theme first — remove unused apps, compress images, switch to a performance-focused theme like Dawn. Consider headless when your ad spend is high enough that even small efficiency gains translate to significant dollar amounts.