Checkout.liquid didn’t die in one shot. Shopify pulled it in two pieces: the Information, Shipping, and Payment pages stopped rendering checkout.liquid on August 13, 2024, and the Thank You and Order Status pages — along with any app still relying on script tags or “additional scripts” there — got the same treatment on August 28, 2025. A year on from the second date, we still get pulled into stores where the first migration was a rush job and the second one is the reason a tracking pixel quietly stopped firing months ago and nobody caught it until the numbers looked wrong. If either of those is you, here’s the actual boundary of what Checkout Extensibility lets you touch now — not the version in Shopify’s marketing copy.

The shape of the new system

Checkout.liquid let you edit the checkout page directly — HTML, CSS, inline script, all of it — but only if you were on Shopify Plus. Everyone else never had that access; the standard hosted checkout was always locked down to a logo and a couple of colors. Checkout Extensibility replaces that single editable template with three systems that barely overlap:

  • Checkout UI extensions — React or plain-JS components you register at fixed injection points (“extension targets”) that Shopify defines. You don’t touch the page; you drop a component into a slot Shopify already built.
  • Checkout branding — a design-token system (color roles, typography, corner radius, button and input styles, per-section color schemes) set through the CheckoutBranding API or the checkout editor’s Branding panel. No stylesheet, no selectors — you’re picking values from a schema.
  • Shopify Functions — small, sandboxed server-side modules, written in TypeScript and compiled to WASM, or in Rust, that run during checkout to change discounts, shipping rates, payment method visibility, or reject the checkout outright with a validation error.

Add Web Pixels for anything that used to be a tracking script pasted into checkout.liquid, and that’s the whole surface. If what you need doesn’t fit one of these four buckets, it’s probably not happening — more on that below.

What you can actually change

More than the “you’re stuck with whatever Shopify gives you” crowd thinks, less than the “it’s basically as flexible as before” crowd tells clients. Concretely:

Visual branding is genuinely deep: accent, canvas, critical, and decorative color roles set independently per section — header, main content, and order summary can each carry a different scheme — plus a font picker pulling from Shopify’s library of a hundred-plus hosted fonts. If none of those match the brand typeface, custom font upload is possible too, via the CheckoutBranding API’s checkoutBrandingUpsert mutation, though Shopify’s own docs warn it costs render performance since the font has to download before checkout text can paint. Button fill vs. outline, corner radius, logo placement. This covers the “make it look like the rest of the site” work that used to eat half of every checkout.liquid ticket, and it survives Shopify’s redesigns instead of breaking on them.

New UI, at fixed points. A checkout UI extension can add a banner, a checkbox, a text field, a collapsible block, an image — Shopify’s own component library, not arbitrary markup — and place it at one of the defined targets: after the header, after the cart line list, before the shipping options, before the Pay Now button, on the Thank You and Order Status pages. The general-purpose block target alone has fourteen placements a merchant can drop it into from the drag-and-drop Checkout Editor. A gift-note field, an age-verification checkbox, a delivery-instructions box, a small “this order supports X” banner — all normal, well-supported work. Here’s roughly what that looks like for a bulk-order notice we’d actually ship:

// shopify.extension.toml
api_version = "2025-10"

[[extensions]]
name = "bulk-order-note"
handle = "bulk-order-note"
type = "ui_extension"

  [[extensions.targeting]]
  target = "purchase.checkout.block.render"
  module = "./src/BulkNote.jsx"
// src/BulkNote.jsx
import {
  reactExtension,
  Banner,
  useCartLines,
} from "@shopify/ui-extensions-react/checkout";

export default reactExtension(
  "purchase.checkout.block.render",
  () => <BulkNote />
);

function BulkNote() {
  const lines = useCartLines();
  const bulk = lines.some((line) => line.quantity >= 12);
  if (!bulk) return null;

  return (
    <Banner title="Bulk order" status="info">
      Orders of 12+ units of one item ship palletized, 5-7 business days.
    </Banner>
  );
}

Business logic moved into Shopify Functions, and this is the one place where extensibility is actually more capable than checkout.liquid ever was, because it runs server-side and deterministically instead of a client-side script racing the page render. Hide a shipping rate when the cart is under a weight threshold. Reorder payment methods for wholesale accounts. Block checkout entirely when a retail customer has a wholesale-only SKU sitting in the cart. Apply a discount that depends on combinations checkout.liquid could never evaluate reliably. It’s more code than the old inline-script approach, and it’s also a lot less likely to quietly break at 2am because Shopify shipped a checkout update.

What’s actually gone, not just moved

This is the part that gets glossed over, and the part that actually burned people. Some checkout.liquid behavior didn’t migrate to a new system — it just isn’t available anymore:

  • Arbitrary DOM and CSS. You can’t select a specific native field and hide it, resize it, or move it with a CSS rule. If Shopify didn’t expose a target or a token for it, it isn’t reachable. A lot of “just nudge this field two pixels” tickets are now “that’s not adjustable, sorry” tickets.
  • Raw script injection. Pasting a tracking snippet straight into checkout.liquid ended for good on August 28, 2025. Tracking now goes through the Web Pixels API, sandboxed in an isolated worker — no DOM access, no window.dataLayer, no arbitrary network calls, just a fixed catalog of events (checkout_completed, product_added_to_cart, and so on) via analytics.subscribe(). Several TikTok, affiliate, and CRO pixel setups we’ve inherited from clients had to be rebuilt from scratch, not reconfigured.
  • Freeform field layouts. You get Shopify’s component set — TextField, Checkbox, Select, Banner, a handful of others — at Shopify’s targets. You cannot build an interaction pattern the component library doesn’t already offer, no matter how reasonable it seems.
  • Flow restructuring. One-page checkout has been the default on every plan since 2023; the older step-by-step layout is still there as a toggle, not a design decision you get to make yourself. You’re not rearranging which fields appear on which step beyond what that toggle allows.

If your checkout.liquid did any of this, there’s no equivalent flag to flip. That work gets rebuilt a different way or it gets dropped, and pretending otherwise to a client just moves the bad news later into the project.

Where we disagree with the “you have full control” pitch

A lot of the migration content out there undersells how real this constraint is, because “don’t worry, extensibility covers everything” is an easier sentence to write than “some of what you had is gone for good.” We’d rather say the second one up front. Shopify traded raw flexibility for a checkout that can’t be broken by a merchant’s own script, that upgrades cleanly, and that Shopify can keep fast and PCI-compliant without auditing everyone’s custom Liquid. That’s a defensible trade. It just isn’t a free one, and treating it as free is how a fixed-bid checkout project goes underwater in week three.

We’ve also mostly stopped recommending the no-code “checkout customizer” apps that showed up once extensibility launched. Most of them are a thin admin UI wrapped around two or three of the same extension targets you can register yourself in an afternoon, for a recurring fee that outlasts the one-time dev cost within a year. If a client needs one field and one banner, we write the extension. If they need six apps’ worth of checkout behavior, that’s usually a sign to slow down and figure out how much of it actually needs to live in checkout at all, versus earlier in the funnel.

And if what a business genuinely needs is checkout behavior this system doesn’t expose — a custom split-payment flow across two processors is the one we hear most — we say so instead of stacking three Functions and a UI extension to fake it. That’s usually the point worth asking whether Shopify’s native checkout, specifically, is still the right fit for that one requirement. WooCommerce hands the checkout template back to you, at the cost of owning its security and upkeep yourself — a fair trade for a business whose checkout logic is genuinely unusual, and a bad one for a normal DTC store that just wants a checkout that stays up.

If you’re migrating now

Don’t port line by line. Audit what checkout.liquid was actually doing and sort it into four piles: pure styling goes to branding tokens, added UI goes to a UI extension, business rules go to a Function, tracking goes to Web Pixels. Whatever’s left after that sort is the genuinely unsupported pile — decide which of it matters enough to solve a different way, and which of it was a workaround for a problem that doesn’t even exist anymore. Budget for a rebuild, not a migration, and you won’t be the one explaining to a client in week four why the timeline just doubled.

Something in here sound like your project?

Tell us what you're building and we'll tell you honestly whether we're a fit.

From the blog

More from the blog

All posts

Leave a Reply

Your email address will not be published. Required fields are marked *