BrainFeed Solutions
Web Design & DevelopmentDigital Marketing & SEOWorkflow Automation & IntegrationContent & BrandingOngoing Support & Optimisation
View all services  →
WorkVenturesAboutInsights
Book a free 30-min consultation  →Book a call
BrainFeed Solutions

Senior-led digital strategy and AI-augmented delivery for service businesses across AU, US and UK.

Services

Web Design & DevelopmentDigital Marketing & SEOWorkflow AutomationContent & BrandingOngoing Support & Optimisation

Company

About UsWorkVenturesInsightsContact Us

Legal

Privacy PolicyTerms & Conditions

Let's talk

hello@brainfeedsolutions.com+91 98986 66600

Ahmedabad, India

AU, US & UK business hours covered

Replies within 1 business day

© 2026 BrainFeed Solutions. All rights reserved.

Privacy PolicyTerms & Conditions
Insights/SHOPIFY ENGINEERING

Shopify checkout extensions vs Functions vs Scripts (RIP): picking the right surface in 2026

Scripts is gone. Every checkout change now routes to one of three layers — UI Extensions, Functions, or Branding. Here's the decision framework we actually use.

Pratik Talati · 13 min read · 3 June 2026

Shopify checkout's three surfaces — UI Extensions (what buyers see), Functions (what Shopify decides), and Branding (how it looks) — with Shopify Scripts shown archived, 2015–2026.

TL;DR — Shopify split checkout into three layers. What buyers see → UI Extensions. What Shopify decides → Functions. How it looks → Branding. Scripts, the thing that used to do all three at once, stops running on 30 June 2026. Pick your surface by what you're actually changing, not by what you used to write a Script for.

If you customise Shopify checkouts, the question you ask in 2026 is no longer "how do I write this?" It's "where does this belong?" That's a better question, and the platform now forces you to answer it before you write a line of code.

For a decade, the answer was Scripts. One Ruby box on a Plus store, and you could change a price, hide a payment method, and inject a banner from the same place. That era is days from over: as of 15 April 2026 you can no longer add or edit Shopify Scripts, and on 30 June 2026 they stop executing entirely. Shopify has said the date won't move.

What replaced Scripts isn't one thing. It's a deliberate split into three surfaces, each with one job. This post is the decision framework we use at BrainFeed to route any checkout request to the right one — built as a tree, a lookup table, and a migration map, because the same idea lands differently depending on whether you're a merchant, an engineer, or an architect.

This article is for:

  • Developers and technical PMs who have a specific checkout change to make and aren't sure which surface owns it
  • Teams mid-migration off Scripts before the 30 June deadline who need to know where each piece lands
  • Anyone who just searched "how do I hide a payment method in Shopify checkout"

This article is NOT for:

  • Plus stores needing a full Checkout Extensibility migration runbook — that's a separate post in our Migration & Scaling pillar
  • Anyone wanting an exhaustive API reference; Shopify's docs do that better than any blog can
  • Readers hoping for a "Shopify is locking everything down" hot take — the split is an upgrade, and I'll argue why

Why Scripts disappeared

Scripts was a single Ruby file that ran inside checkout on Plus stores. It could rewrite line-item prices, reorder or hide shipping and payment methods, and drop markup onto the Thank You and Order Status pages. One surface, total reach — the appeal and the problem in one.

That reach is what made checkout impossible for Shopify to improve. If any Script could run arbitrary code against the page, Shopify couldn't upgrade or harden checkout without risking every customisation built on top of it — performance and security held hostage by a feature designed to do anything, anywhere.

So Shopify drew boundaries. The timeline, precisely:

  • 28 August 2025 — checkout.liquid for the Information, Shipping, and Payment steps was sunset. Stores that hadn't moved are being auto-upgraded.
  • 15 April 2026 — Scripts became read-only. No new Scripts, no edits to existing ones.
  • 30 June 2026 — Scripts stop running. Any pricing, shipping, payment, or bundle logic still living in a Script breaks on 1 July.
  • 26 August 2026 — non-Plus stores lose the Additional Scripts, Thank You, and Order Status script boxes too.

The lesson worth carrying out of the Scripts era isn't "Shopify took my toy away." It's that Scripts let you put logic and presentation in the same box, and that convenience became a category error. The new model makes you separate the two — and the rest of this article is why that separation is the point.

The three modern layers

Checkout UI Extensions are how you change what the buyer sees. They're React (or JS) components that render at fixed extension points in the checkout, customer account, and post-purchase flows. They run in a sandboxed Web Worker with no access to the DOM, the window, or sensitive payment data — they render through Shopify's component APIs, not into the page directly. This is the layer for fields, messages, banners, upsells, and anything informational or lightly interactive.

Shopify Functions are how you change what Shopify decides. They're small programs compiled to WebAssembly (Rust, or JavaScript via Javy) that run on Shopify's servers as part of checkout's own logic: computing discounts, hiding or reordering delivery and payment methods, validating the cart, and transforming line items for bundles. They take a GraphQL input query and return a set of operations. They are pure logic — no UI, and crucially no network access.

Branding is how you change how it looks. Colours, fonts, logo, corner radius, button styles — configured through the checkout editor and the Branding API, not written as code. If the request is "make it match our brand," this is the layer, and reaching for an extension to do it is a mistake.

The capability matrix

If you remember one image from this article, make it this. Five seconds tells you where anything belongs:

CapabilityUI ExtensionsFunctionsBranding
Show / add content to checkout✓✗✗
Run business logic✗✓✗
Modify shipping / delivery options✗✓✗
Modify payment options✗✓✗
Apply / compute discounts✗✓✗
Block / validate checkout✗✓✗
Make network calls✓ (opt-in)✗✗
Change colours / fonts / logoLimited✗✓

The matrix is also the argument. Almost every row has exactly one ✓. The surfaces barely overlap — and where they do (network calls in UI Extensions, limited styling), it's deliberate, not redundant.

The decision tree

Route by the job you're trying to do:

  • Changing what the buyer sees — a field, a message, a banner, an upsell → UI Extension
  • Changing a discount — tiered, bundle, conditional → Discount Function
  • Changing shipping/delivery — hide, rename, reorder methods; pincode rules → Delivery Customization Function
  • Changing payment — hide, rename, reorder methods; threshold rules → Payment Customization Function
  • Blocking or validating the cart — min order, address rules, B2B gates → Cart & Checkout Validation Function
  • Bundling — merge or expand line items → Cart Transform Function
  • Look-and-feel only — colours, fonts, logo → Branding (no code)

The last branch is the one people miss. Logic and its explanation are now two surfaces by design: the Function decides, the UI Extension narrates. That feels like extra work until the first time you change the logic without touching the copy, or vice versa.

Ask the right question first

The whole framework turns on the question you ask before you build:

Wrong question: "How do I hide Cash on Delivery?" Right question: "Am I changing payment logic or checkout UI?"

Hiding COD is a payment-logic change → Payment Function. Name the surface first and the implementation follows. Ask the wrong question and you'll lose an afternoon trying to hide a payment method from a UI Extension that was never allowed to touch one.

Where your old Scripts go

If you're migrating off Scripts before 30 June, this is the map. Every Script use case has exactly one new home:

Old Script use caseNew home
Line-item / order discountsDiscount Function
Tiered or conditional shipping ratesDelivery Customization Function
Hiding or reordering payment methodsPayment Customization Function
Bundle / "buy X get Y" logicDiscount Function + Cart Transform
Cart validation / purchase rulesCart & Checkout Validation Function
Checkout messaging / bannersUI Extension
checkout.liquid stylingBranding

Common requests, mapped

This is the lookup the rest of the article exists to support. Nobody searches for "checkout surfaces" — they search for the thing they need to ship today. Find the row, ship the right layer.

RequirementSurface
Add a delivery-note or gift-message fieldUI Extension
Show loyalty points / store-credit balanceUI Extension
Show a custom upsell or cross-sell blockUI Extension
Display a shipping-ETA or trust messageUI Extension
Explain why a discount appliedUI Extension (+ Function)
Hide Cash on Delivery above ₹10,000Payment Function
Reorder or rename payment methodsPayment Function
Hide express shipping for certain pincodesDelivery Function
Rename a shipping optionDelivery Function
Tiered / volume discountsDiscount Function
Buy-X-get-Y or bundle pricingDiscount Function + Cart Transform
Block PO box / restricted addressesValidation Function
Enforce a minimum order valueValidation Function
Validate GST / VAT number formatValidation Function
Restrict checkout by customer tag (B2B)Validation Function (+ UI Extension)
Merge or expand bundle line itemsCart Transform Function
Change checkout colours, fonts, or logoBranding
Adjust button style or corner radiusBranding

Bookmark-worthy, and worth saying once: if a row pulls you toward two surfaces, that's not the framework failing — it's the seen-versus-decided split showing through.

The scorecard

The tree tells you which. This tells you why, and where each surface stops:

SurfaceRuns whereBest forHard limitsWrong tool when
UI ExtensionsBuyer's browser, sandboxed Web WorkerWhat the buyer sees; light interactionNo DOM/window; checkout-step extensions are Plus-only; can't touch pricing/shipping/payment logicYou need to change a rule or a price
FunctionsShopify's servers, WebAssemblyDiscounts, delivery/payment rules, validation, bundlesNo network/IO; deterministic; per-store count limits; renders nothingYou need to show or explain something
BrandingCheckout editor / Branding APIColours, fonts, logo, radiusStyle only, within Shopify's tokens; no logic, no new contentYou need behaviour, not appearance
Scripts (RIP)Legacy Plus checkout— historical —Read-only since 15 Apr 2026; stops 30 Jun 2026Always, now — migrate off

What bites you in production

These constraints trip up teams arriving from the Scripts world. Read them not as complaints but as the shape of the bargain: each limit is the price of a checkout Shopify can keep making faster and safer underneath you.

Functions can't reach the network — on purpose. No fetch, no database, no external pricing service mid-checkout. A Function takes its GraphQL input and returns operations; that's the whole contract. It feels restrictive until you realise it's why a Function can run inside checkout's hot path without becoming a latency or reliability risk. If a decision genuinely needs external data, you precompute it (metafields, tags, cart attributes set earlier) and let the Function read that. Determinism isn't a tax here — it's what makes the checkout fast.

UI Extensions can't see the page — also on purpose. Sandboxed in a Web Worker, no DOM, no window, no access to payment fields. You don't style the checkout from an extension and you can't scrape it; you compose through Shopify's component and API surface. That isolation is exactly what lets Shopify restyle and re-lay-out checkout without breaking your extension. UI Extensions can make network calls, but only opt-in: you set network_access = true in the extension's capabilities, and the call goes to your app backend, not arbitrary origins.

Functions have count limits, and an execution order. As of mid-2026 a store runs at most 5 Discount Functions, 1 Cart Transform, and 5 Validation Functions, and they execute in a fixed order — transforms and pricing first, then discounts, then validation. Architect for that order; two Functions fighting over the same outcome is a self-inflicted wound, not a platform bug.

The Plus line moved, and it's the most common surprise. Functions — discounts, delivery, payment, validation — work on every plan. But UI Extensions on the checkout steps (Information, Shipping, Payment) are Plus-only; non-Plus stores get extensions on the Thank You and Order Status pages. So a non-Plus merchant can hide COD over a threshold with a Function but can't add a field to the payment step. Confirm the plan before you promise the UI.

The "just do it in Liquid" reflex is gone. There is no checkout.liquid to drop a snippet into anymore. The muscle memory of "I'll just edit the template" has no target. That reflex is the single biggest source of wasted hours for teams that haven't internalised the new model — which is the whole reason a decision framework beats another how-to.

What the code actually looks like

Two short examples to make the split concrete. First, a UI Extension that adds a delivery-note field — pure presentation, written in React against the 2026-04 API:

// extensions/delivery-note/src/Checkout.jsx
import {
  reactExtension,
  TextField,
  useApplyAttributeChange,
} from "@shopify/ui-extensions-react/checkout";

export default reactExtension(
  "purchase.checkout.delivery-address.render-after",
  () => <DeliveryNote />,
);

function DeliveryNote() {
  const applyAttributeChange = useApplyAttributeChange();
  return (
    <TextField
      label="Delivery note (optional)"
      multiline={3}
      onChange={(value) =>
        applyAttributeChange({ type: "updateAttribute", key: "delivery_note", value })
      }
    />
  );
}

Notice what it doesn't do: it doesn't price anything, doesn't touch payment, doesn't read the DOM. It renders and writes a cart attribute. That's the ceiling of a UI Extension, by design.

Now the logic half — a Payment Function that hides Cash on Delivery above ₹10,000. The decision lives entirely server-side:

# extensions/hide-cod/input.graphql
query Input {
  cart { cost { totalAmount { amount } } }
  paymentMethods { id name }
}
// extensions/hide-cod/src/run.js
export function run(input) {
  const total = parseFloat(input.cart.cost.totalAmount.amount);
  const cod = input.paymentMethods.find((m) => m.name === "Cash on Delivery");

  if (total <= 10000 || !cod) return { operations: [] };
  return { operations: [{ hide: { paymentMethodId: cod.id } }] };
}

No fetch, no database, no UI — input in, operations out. If you also wanted to tell the buyer why COD vanished, that's the other surface: a UI Extension reading the same threshold. Two files, two layers, one behaviour. That's not duplication; that's the architecture doing its job.

When you shouldn't customise at all

The strongest checkout decision is often don't. A few cases where the right surface is "none":

Don't customise because you can. The most common failure we see isn't picking the wrong surface — it's building a customisation that shouldn't exist. The delivery-note field nobody reads. Six upsells stacked into the payment step. A validation rule that blocks 2% of legitimate orders to stop 0.1% of bad ones. Every element you add to checkout is friction, and the highest-converting checkout is almost always the simplest one. "Which surface?" should always be preceded by "should this exist?"

Don't hand-roll what an app already does well. If a battle-tested App Store app covers the discount or validation, a custom Function is maintenance you've volunteered for. Build custom when the logic is genuinely yours; install when it isn't.

Don't customise before you've migrated. If you're still on Scripts, the prerequisite isn't a new feature — it's getting onto Checkout Extensibility before 30 June. Build the new thing after the old thing is safely moved, not on top of a surface that's about to stop running.

Don't write code to change a colour. If the request is visual, it's Branding. Reaching for an extension to restyle checkout is the inverted version of the old Scripts mistake — using a logic/UI tool for a job the styling layer already owns.

The thesis

Scripts hid a category error. It let you keep logic, presentation, and styling in one box, and because it was convenient, nobody noticed the cost — until Shopify couldn't improve checkout without permission from a million bespoke Ruby files.

The three-layer split fixes that at the root. What buyers see, what Shopify decides, and how it looks are genuinely different concerns, and they now live in genuinely different places. The constraints people grumble about — Functions with no network, extensions with no DOM — aren't friction bolted on. They're the boundaries that let Shopify make checkout faster, safer, and upgradeable underneath you, without ever asking what you built on top.

So the skill in 2026 isn't knowing the Functions API by heart. It's answering "where does this belong?" before you write anything — and trusting that if the answer feels like two surfaces, that's the platform telling you you've got two concerns. Pick by the axis, not by habit.

If you'd rather hand the whole migration-and-customisation problem to a team that already thinks this way, that's the work we do: Shopify development for SMB merchants.


Next in this series: the full Scripts-to-Functions migration — what we move first, how we test a Function before it touches live checkout, and the order-of-operations traps. After that: a Functions deep-dive on the input-query pattern and how to keep logic deterministic when the business rules aren't.

On this page

  • Why Scripts disappeared
  • The three modern layers
  • The capability matrix
  • The decision tree
  • Ask the right question first
  • Where your old Scripts go
  • Common requests, mapped
  • The scorecard
  • What bites you in production
  • What the code actually looks like
  • When you shouldn't customise at all
  • The thesis

Share

Pratik Talati

Founder, BrainFeed Solutions

15 years shipping product. Senior-led teams, AI-augmented delivery, AU & US clients. I write about the things we ship — and the things we wish we hadn't.

Follow Pratik

Get new posts the day they go up.

Follow on LinkedInMore articles by Pratik  →

Keep reading

Related articles

next-16-vs-remix-cover

BUILD DECISIONS

Why we picked Next 16 over Remix for our rebuild (and what immediately broke)

We seriously evaluated Remix. We still picked Next 16. The framework choice mattered less than we expected. The migration mattered more.

Pratik · 11 min read · May 27, 2026

Mobile app development cost 2025 guide showing pricing range from $10,000 to $500,000+

WEB & DESIGN

Mobile Application Development Cost Breakdown: Complete 2025 Pricing Guide

Planning to build a mobile app in 2025? Understanding mobile application development cost is crucial for making informed decisions and setting realistic budgets. Whether you’re a startup founder, business owner, or entrepreneur, this comprehensive guide breaks down everything you need to know about app development pricing in 2025. What Determines Mobile Application Development Cost? The

Newsletter

Enjoyed this? Get the next one in your inbox.

Two emails a month. Plain-language playbooks. Unsubscribe anytime.

No spam. We respect your privacy — see our Privacy Policy.

Want to apply this to your business?

Book a free 30-min consultation. We'll talk about your specific situation.

Book a free 30-min consultation
Logic and matching UI — e.g. a custom discount and a line explaining it → Function + UI Extension together

Pratik · 4 min read · September 14, 2025

Shopify shopping bag with growth arrow and custom development sign — signs your store has outgrown basic Shopify

WEB & DESIGN

10 Signs Your Business Has Outgrown Basic Shopify and Needs Custom Development

Starting with a basic Shopify plan is smart for most new businesses. It’s cost-effective, easy to set up, and gets you selling quickly. But as your business grows, you might notice certain limitations holding you back from reaching your full potential. If you’re processing thousands of orders monthly, managing complex inventory, or serving enterprise clients,

Pratik · 10 min read · September 3, 2025

Browse by topic

  • Build Decisions
  • Shopify Engineering
  • SaaS Infrastructure & Payments
  • Migration & Scaling Stories
  • Digital Strategy
  • SEO & Content
  • AI & Automation
  • Healthcare
  • Recruitment
  • Web & Design