Skip to content
aviral gupta

Headless Shopify vs Custom MERN Ecommerce: How to Choose

Choose headless Shopify unless your pricing, quoting or checkout logic genuinely cannot live inside its checkout, because a custom MERN stack ecommerce build buys you that freedom and hands you PCI scope, fraud, tax and payment rails as permanent work.

Written by Aviral GuptaPublished 10 min read
  • Ecommerce
  • Headless commerce
  • Next.js
  • MERN
  • GCC payments
Headless Shopify vs Custom MERN Ecommerce: How to ChooseSTOREFRONT OWNED · COMMERCE RENTEDNext.js storefrontSSR + ISRAPI layercart · search · authcatalogueShopify / custompaymentsStripe / TelrCMSheadless

Headless Shopify vs custom ecommerce: what are you actually trying to unblock?

Almost everyone who asks me about headless Shopify vs custom ecommerce has already decided the theme is the problem. It usually isn't. The theme is the part you can see, so it absorbs the blame for everything you can't.

Before you price anything, write down the one thing your store cannot do today. Not the wish list. Then work out which layer it lives in, because the layer decides the build.

  • Presentation — speed, layout, merchandising, content, campaign pages. A Next.js storefront on the Shopify Storefront API fixes this and nothing underneath needs to move.
  • Catalogue and pricing logic — customer-specific prices, quote-then-order, bundles priced by rule rather than by SKU. This sits below the storefront, so going headless does not touch it.
  • Checkout — deposit plus balance, split payments, a cash-on-delivery flow with its own confirmation step. This is the hardest boundary of the three and the one most proposals quietly skip.
  • Operations — stock across branches, returns, finance exports, and who logs in on a Monday to fix a wrong price without opening a ticket.

If your one thing is presentation, headless commerce with Next.js is the cheap answer and you should stop reading vendor pitches for anything bigger. If it is pricing logic, checkout or operations, you are choosing between contorting Shopify and owning a platform.

What does Shopify give you for free that people underestimate?

Shopify's real product is not the storefront. It is the pile of liabilities it absorbs on your behalf, and every one of them comes back onto your invoice the day you leave.

  • PCI scope. Card data never reaches your infrastructure, so you sit in the lightest self-assessment tier instead of running an audit programme.
  • Fraud and chargebacks. Screening, 3-D Secure orchestration and a dispute workflow that someone non-technical can operate.
  • Tax. VAT on the right lines, tax-inclusive display for GCC shoppers who expect the shelf price to be the paid price, and invoices that survive a question from the tax authority.
  • Payment rails and reconciliation. Settlement files, partial refunds, currency handling, and a ledger your accountant already understands.
  • Inventory and order state. One source of truth for stock, holds, cancellations and fulfilment, with a documented webhook per transition.
  • An admin your client can actually use. The least glamorous item here and the one that decides whether the build is still loved in month thirteen.

Rebuild those honestly and you have written most of a small ERP — the work nobody quotes, because it stays invisible until the first refund goes wrong.

Layered diagram: a Next.js storefront calls an API layer for cart, search and auth, which fans out to a catalogue, a payment provider and a headless CMS.STOREFRONT OWNED · COMMERCE RENTEDNext.js storefrontSSR + ISRAPI layercart · search · authcatalogueShopify / custompaymentsStripe / TelrCMSheadless
The headless split: you own rendering and the API layer, you rent catalogue, payments and content. Where you draw the payment boundary is the whole decision.

Where does headless Shopify actually stop?

Here is the fact most comparisons bury: going headless does not give you a custom checkout. You can replace every pixel of the storefront and still hand the shopper to Shopify's checkout, which you extend through sanctioned extension points rather than rewrite. Plan around that ceiling.

  • Bespoke pricing logic. Contract prices per account, tiered volume breaks, price books differing by market and currency at once.
  • B2B quoting. Request a quote, negotiate lines, approve, convert to an order on purchase-order terms with a credit limit enforced at the door.
  • Complex bundles and configurators. Assemblies priced by rule, component-level stock, and a validity check that must run before the cart will accept the line.
  • Non-standard checkout. Deposit now and balance on delivery, milestone payments, hire agreements, or an identity check between authorisation and capture.
  • Per-market catalogues. Different SKUs, different regulatory copy and genuinely different Arabic descriptions per market, not one catalogue with a language switch.
  • Platform fees at scale. A percentage of gross merchandise value keeps growing after your engineering cost has flattened. That crossover is arithmetic — do it on your own numbers before anyone does it for you.

What does a custom MERN ecommerce build really cost you?

A MERN stack ecommerce build is not Shopify minus the fees. It is a standing commitment to keep a money-handling system alive. Node, Express and MongoDB are the easy part — catalogue, cart and orders run in a week. The permanent line items are these.

  • PCI posture. Keep card entry inside the gateway iframe or hosted fields. The moment a field on your domain touches a card number, your compliance burden changes category.
  • Idempotency. Gateways retry webhooks. Shoppers double-click. Without an idempotency key on capture and a real order state machine, you will refund your way to the lesson.
  • Stock concurrency. Two shoppers, one unit, one traffic spike. Reserve stock inside the transaction that creates the order, and load-test it before a seasonal peak rather than during one.
  • Tax and invoicing. Sequential invoice numbers, credit notes, and the UAE e-invoicing programme: pilot from 1 July 2026, businesses over AED 50m live 1 January 2027 and smaller businesses from 1 July 2027 (Avalara, 2026).
  • Admin, roles and audit trail. Somebody will edit a price at 2am. You need to know who, and be able to undo it.
  • Patching and restore. Dependency upgrades, a backup you have actually restored from, and an answer for the night checkout fails.

The build pays when the data model is the product. Rental and booking inventory is the clearest example, and the one I worked on daily: the unit is an asset on a date range, not a quantity on a shelf, and no commerce platform models that natively. Marketplaces with independent sellers, and B2B catalogues where price is a function of the account, are the same shape. If that is you, a MERN stack application is the only honest fit.

How do GCC payment rails and cash on delivery change the answer?

This is where generic comparisons stop being useful. UAE e-commerce is projected at USD 12.30bn in 2026 rising to USD 21.01bn by 2031, an 11.29% CAGR (Mordor Intelligence, 2026), and roughly one in every two dirhams of consumer spend now transacts online. The rails carrying that money are not the ones a US-authored comparison assumes.

  • Local gateways are the default, not the fallback. Telr, Network International, PayTabs, Tap and Checkout.com all serve UAE merchants; Telr publishes among the lowest UAE-focused rates at 2.49% plus AED 1 (Skimbox UAE gateway comparison, 2026).
  • Onboarding time is a project risk. Stripe and Tap typically approve in one to three business days, while Amazon Payment Services and Network International take one to two weeks (Skimbox, 2026). Apply in week one, not the week before launch.
  • Mada decides your Saudi expansion. PayTabs, Tap, Telr and Network International support Mada; Stripe does not support it directly (Skimbox, 2026). That line changes your gateway choice the day a Dubai store sells into Riyadh.
  • Cash on delivery is a first-class flow, not a checkbox. It needs its own order state, a driver settlement event, a reconciliation report and a failed-delivery path that returns stock. Bolt it on and your revenue numbers are fiction.
  • Seasonal peaks are extreme. GCC e-commerce runs 280–360% above baseline in the ten days before Eid al-Fitr (Memob, 2026). Whatever you build has to survive that week, and nothing should launch during it.

What do an Arabic RTL catalogue and VAT demand from each stack?

An Arabic catalogue is a content decision before a technical one. Around 63% of UAE internet users prefer Arabic for local purchase decisions, yet fewer than 20% of Dubai businesses maintain optimised Arabic content (Andava UAE digital marketing statistics, 2026). That gap is commercial, and it is the strongest argument for owning the storefront in either model.

The engineering half is testable. Set lang and dir per locale, use CSS logical properties so padding mirrors instead of breaking, and subset your Arabic webfont — Noto Kufi Arabic and IBM Plex Sans Arabic both cause LCP and CLS regressions when shipped whole. Arabic product titles are not translated English titles; shoppers search differently, so catalogue copy is authored, not passed through a translation API.

Shopify Markets and Translate & Adapt give you locale routing and per-market pricing without code, which is a lot. What they do not give you once you go headless is URL discipline. Variant URLs are the most common headless SEO failure of the lot: every colour and size generating an indexable near-duplicate, none canonicalised. The Liquid theme emitted correct canonicals for free; your Next.js storefront will not unless you write them, meaning one canonical per product, variants as parameters that self-canonicalise upward, and a sitemap listing products rather than permutations. That transferred work is what SEO-safe rebuilds exist to cover.

VAT is the quieter half. UAE VAT at 5% has to appear the way shoppers here expect it — tax-inclusive display, compliant invoice attached to the order. Shopify does this across markets out of the box. In a custom build it is a tax service you own, with rounding rules, credit notes and an invoice sequence a finance audit can follow.

How do you keep purchase events honest in either model?

Both models break tracking in the same place: the moment money is confirmed is not the moment the browser is on your page. On headless Shopify the checkout runs on Shopify's domain, so a browser-only purchase pixel is unreliable by construction — the dependable path is the orders/paid webhook into your own route handler. On a custom build you do own the confirmation route, so you can fire twice and count twice unless you deduplicate deliberately.

  • One id per order, generated server-side. Use it as the Meta eventID in the browser and event_id in the Conversions API payload, and as the GA4 transaction_id in both gtag and the Measurement Protocol. Same string both sides, or deduplication silently fails.
  • Fire on capture, not on redirect. A thank-you page is a navigation event, not a payment. For COD, the purchase event belongs to the delivery settlement, which may be four days later.
  • Handle refunds explicitly. GA4 accepts a refund event carrying the same transaction_id. Meta has no refund event, which is the real reason not to report revenue you have not yet collected.
  • Verify, do not assume. Meta Events Manager Test Events shows a deduplicated badge when the pair matches; GA4 DebugView shows the server hit arriving. Run both with an ad blocker enabled, because that is the traffic you are trying to recover.
import {createHash} from 'node:crypto';

export const runtime = 'nodejs';
export const dynamic = 'force-dynamic';

// Pin the Graph API version you tested against; do not float it.
const GRAPH = 'https://graph.facebook.com/v21.0';

type Purchase = {
  orderId: string; // your order number — the only id that matters
  value: number;
  currency: string; // AED, SAR, QAR
  email?: string;
  clientId: string; // GA4 client_id, read from the _ga cookie
  fbp?: string;
  fbc?: string;
  sourceUrl: string;
};

const sha256 = (value: string) =>
  createHash('sha256').update(value.trim().toLowerCase()).digest('hex');

export async function POST(request: Request) {
  const order = (await request.json()) as Purchase;

  // The browser sent exactly this string as fbq(..., {eventID}).
  const eventId = `purchase.${order.orderId}`;

  const meta = fetch(`${GRAPH}/${process.env.META_PIXEL_ID}/events`, {
    method: 'POST',
    headers: {'content-type': 'application/json'},
    body: JSON.stringify({
      access_token: process.env.META_CAPI_TOKEN,
      data: [
        {
          event_name: 'Purchase',
          event_id: eventId, // ← dedup key, must equal the pixel eventID
          event_time: Math.floor(Date.now() / 1000),
          action_source: 'website',
          event_source_url: order.sourceUrl,
          user_data: {
            em: order.email ? [sha256(order.email)] : undefined,
            fbp: order.fbp,
            fbc: order.fbc
          },
          custom_data: {currency: order.currency, value: order.value}
        }
      ]
    })
  });

  const ga4 = fetch(
    'https://www.google-analytics.com/mp/collect' +
      `?measurement_id=${process.env.GA4_MEASUREMENT_ID}` +
      `&api_secret=${process.env.GA4_API_SECRET}`,
    {
      method: 'POST',
      body: JSON.stringify({
        client_id: order.clientId,
        events: [
          {
            name: 'purchase',
            params: {
              transaction_id: order.orderId, // ← GA4 dedups purchases on this
              currency: order.currency,
              value: order.value
            }
          }
        ]
      })
    }
  );

  // Never let a pixel outage fail an order confirmation.
  await Promise.allSettled([meta, ga4]);
  return Response.json({ok: true});
}
src/app/api/purchase/route.ts — one order id becomes the deduplication key for both platforms.

This matters more here than most places. UAE average Google Ads cost per click runs 20–40% above global averages (Logicworks, 2026), so every purchase you double-count or lose is priced well above the global norm. The event layer is a smaller job than either build and the one I sequence first — it is the core of my analytics and tracking work.

Headless Shopify or custom MERN: which one should you pick?

Same storefront technology on both sides. The difference is who owns the money path.
DimensionHeadless Shopify + Next.jsCustom MERN build
Time to a live storefrontWeeks — catalogue, cart and checkout already existMonths — every state transition is written and tested
Checkout controlExtension points only; the flow itself is fixedTotal, including deposits, milestones and COD settlement
PCI scopeLightest tier, permanentlyLightest tier only if card entry stays inside the gateway iframe
Bespoke pricing and B2B quotingFights you, or lives in a bolt-on appNative — this is the reason to build
VAT and invoicingHandled per market out of the boxYou own rates, rounding, credit notes and invoice sequence
Local gateways and CODSupported through apps and manual payment methodsDirect integration; COD modelled as a real order state
Arabic and per-market catalogueMarkets plus Translate & Adapt; you still own canonicalsAnything you can model, at the cost of building it
Ongoing cost shapePercentage of GMV, rising with revenueFixed engineering capacity, rising with complexity
Who fixes a wrong price on SundayYour client, in an admin they already knowYou, unless you built and documented the admin properly

Read it as a disqualifier, not a scorecard. If nothing in the middle column stops you, headless Shopify on Next.js is the rational build.

How would I scope either build?

  1. 1

    Write the blocking requirement as a test

    Not "we need flexible pricing" but "a contract-priced account adds two configured units, sees the negotiated total, and the invoice shows 5% VAT on the discounted line". A requirement you can execute is one you can price.

  2. 2

    Prototype it against Shopify for two days

    A development store, the Storefront API, the checkout extension points. Either the test passes or it fails for a reason you can write down. Cheapest decision in the project.

  3. 3

    Open the gateway applications immediately

    Whichever way the prototype goes, merchant onboarding runs from one to three business days up to one to two weeks (Skimbox, 2026). Longest lead time you have, and it costs nothing to start.

  4. 4

    Build the event layer before the storefront

    Order id, deduplicated purchase event, refund path, COD settlement trigger. Ship it behind the existing store so you have clean before-and-after data instead of a launch you cannot evaluate.

  5. 5

    Fix the SEO surface in the same sprint

    Canonical per product, variants as self-canonicalising parameters, Product and Offer structured data emitted from your own data, and a product-level sitemap. Retrofitting this after launch is where rankings go.

I build the custom side, not the Shopify side. That is worth saying plainly, because it means the recommendation above costs me the work rather than winning it: if your catalogue and your checkout fit inside Shopify, the honest answer is to stay there and spend the saved budget on merchandising. Where I can help is the other path, and the boundary cases where a storefront has to do something a hosted checkout will not. If you want that scoped, my ecommerce development work starts with an introductory project call. I am in Dubai on UTC+4, so get in touch.

What should you know about how I work on this?

Start from the one thing your current store cannot do, then find which layer it lives in: presentation, catalogue and pricing logic, checkout, or operations. Presentation problems are solved by a headless storefront on your existing platform. Pricing and checkout problems are the only honest reasons to build custom. Everything else — speed, design, campaign pages, Arabic content — is achievable on Shopify with a Next.js front end. Platform choice follows the blocking requirement; it should never be chosen first and justified afterwards.

Shopify rents you the money path: PCI scope, fraud screening, tax, payment rails, inventory state and an admin your staff can use. A custom build means you own all of it, permanently. The visible storefront can be identical — the same Next.js code can render either. The difference shows up in refunds, chargebacks, VAT invoices, stock concurrency during a peak, and who gets called when checkout fails at midnight. Choose custom when your business model genuinely cannot be expressed inside a fixed checkout.

Materially less than a custom build, because catalogue, cart, checkout and admin already exist. The work is the storefront, the Storefront API integration, the SEO surface you inherit (canonicals, variant URLs, structured data, sitemaps) and the event layer. A custom MERN platform is a different order of magnitude, since every order state transition has to be written and tested. I fix scope and timeline after an introductory project call rather than quoting a duration before I have seen your catalogue and checkout requirements.

For the UAE and wider GCC: Telr, Network International, PayTabs, Tap, Checkout.com and Stripe are the practical set, alongside cash on delivery as a first-class flow. Telr publishes among the lowest UAE-focused rates at 2.49% plus AED 1 (Skimbox, 2026). Selection is usually driven by two constraints rather than headline rate: onboarding time, which runs from one to three business days up to one to two weeks depending on the provider, and Mada support, which PayTabs, Tap, Telr and Network International offer but Stripe does not directly.

You do, on both paths. On a custom build, the storefront repository, the API layer, the tracking route handlers and any data model I write are yours on final payment, in your own Git organisation from day one rather than handed over at the end. On headless Shopify you keep your Shopify account, your data and your apps whoever builds the storefront, and you should ask whoever does for the same repository terms in writing before work starts. If you later replace me, everything needed to keep running is in the repository and its README, which is a deliberate design goal, not a courtesy.

That is the core risk in any replatform, and it is managed rather than promised. The method is a full crawl and archive of the current URL set before anything changes, a redirect map built from that crawl rather than from a sitemap, canonical discipline for product variants, structured data reproduced on the new templates, and index monitoring for weeks after launch. I have run a PHP Laravel to Next.js migration with no ranking loss using exactly this sequence. No one can guarantee rankings, but most migration losses come from a small set of avoidable errors.

Yes, and the shape of it differs by model. On headless Shopify, ongoing work is mostly storefront and campaign work; the platform handles security and payment updates. On a custom MERN build, maintenance is not optional: dependency patching, gateway API changes, backup restores that have actually been tested, and someone reachable when checkout fails. I say this before the build, not after, because underestimating it is the most common reason a custom commerce platform quietly rots in its second year.

On headless Shopify, yes. Your team keeps the Shopify admin for products, prices, stock and orders, and a headless CMS should be connected for editorial content so marketing does not need a deployment to change a banner. On a custom build, admin usability is a line item you have to fund deliberately, because it is the first thing cut under pressure and the first thing missed in production. Ask any prospective developer to demo the admin, not just the storefront, before you sign.

// OPEN TO WORK

Hiring, or building something that needs an engineer?