Skip to content

How to Show a Quantity-Break Discount Table on Your Shopify Product Page (Without Breaking SEO)

By Stackable TeamPublished on August 5, 2026Shopify How-To#quantity-breaks#volume-discounts
How to Show a Quantity-Break Discount Table on Your Shopify Product Page (Without Breaking SEO)

To show a quantity or volume discount table on a Shopify product page, you either add a theme app block from a discount app in the theme editor, or you hand-code the table in Liquid inside your product template. The app-block route is the no-code path: you drop the block under the add-to-cart button and it renders a "buy more, save more" tier table without touching theme files. The part that actually matters is what happens after you place it. The displayed table has to stay in sync with the discount that fires at checkout, and it has to render SEO-safe, without wrapping prices in heading tags that create extra H1s and quietly damage your rankings. Get those two things right and a tier table is one of the highest-leverage additions you can make to a product page. Get them wrong and you are showing customers prices that checkout will not honor, on a page whose heading structure you just broke.

This guide covers why a visible tier table lifts average order value, the do-it-yourself Liquid approach and the maintenance traps that break it, theme app blocks versus hard-coding, the real SEO danger of rendering prices as heading tags, Core Web Vitals considerations, and a full worked example with a "quantity, price each, you save" table you can copy.

Why show a quantity-break table on the product page at all?

A quantity break is a tiered discount: buy 3 and save 10 percent, buy 6 and save 15 percent, buy 12 and save 20 percent. The discount can exist perfectly well without any storefront display. Shopify will apply it at checkout whether or not the customer ever sees it coming. That is exactly the problem. A discount the shopper cannot see is a discount that cannot change their behavior.

The tier table is the merchandising half of the offer. It turns a silent price rule into a visible invitation. A customer who came to buy one unit sees, in plain numbers, that buying three drops the per-unit price, and buying six drops it further. That is a nudge toward a bigger cart, placed at the exact moment of decision, right under the add-to-cart button.

There is a well-understood behavioral reason this works. A tier table sets an anchor and then shows the "you save" delta against it. The shopper is no longer evaluating one price in isolation; they are comparing their current choice to a visibly better one that is one or two units away. For consumable, stockable, or gift-friendly products, that comparison reliably moves units. Wholesale and B2B buyers expect a price break grid as a matter of course, and its absence reads as a missing feature.

The catch is that the table only helps if shoppers trust it. The first time a customer sees "6 for 34 dollars each" on the product page and then gets charged 40 dollars each at checkout, you have not just lost that order. You have taught them your prices are not real. Which is why the display and the discount engine cannot be two separate systems that happen to agree today.

IMAGE PLACEHOLDER (Image 1): A quantity-break tier table on a product page
Suggested visual: A clean 16:9 storefront mockup of a Shopify product page. Right column shows product title, price, and an add-to-cart button. Directly below the button sits a 4-row tier table with columns "Quantity," "Price each," and "You save." Row three (6 to 11) is highlighted in violet as the active tier, with a small "You are saving 15%" note. Neutral product photo placeholder on the left. Brand colors violet and gold accents, flat modern UI, no real logos.

What are the ways to display a tier table on a Shopify product page?

There are three practical routes, in rough order from most manual to most maintainable.

Option 1: hand-code the table in Liquid

You edit your product template (or a section or snippet it renders) and write the table markup yourself. You either hard-code the tier values as static HTML, or you read them from a product metafield and loop over them in Liquid. This gives you total control over the markup and styling, and it is genuinely the right answer for a store with one or two products and a developer on call.

The cost is maintenance, and it is higher than it looks. A hard-coded table is a second copy of your pricing that lives in your theme, disconnected from the discount that actually runs. Every time you change a tier, you change it in two places: the discount and the theme. Miss one and the table lies. Merchants on the Shopify forums describe exactly this pattern, building a hand-built HTML table for the tier display and then fighting to keep it aligned with a discount rule that lives somewhere else entirely. It works until the day it silently does not.

Option 2: a metafield-driven Liquid table

A more robust do-it-yourself version stores the tier data in a product metafield and renders it with a Liquid loop, so the table is data-driven rather than hard-coded per product. This is better; you edit tiers in one structured place. But you still own the rendering, the responsive styling, the live-update JavaScript, and crucially the job of keeping the metafield values equal to the values your discount uses. Nothing enforces that equality for you.

Option 3: a theme app block from a discount app

The no-code route uses Shopify's theme app extension framework. According to Shopify's theme app extensions documentation, these extensions "allow merchants to easily add dynamic elements to their themes without having to interact with Liquid templates or code," and the docs list "prices, ratings" among the dynamic elements they are built for. A tier table is precisely this kind of element.

The app ships an app block. In the theme editor you open your product template, click Add block in a section, pick the app's tier-table block, and position it wherever you want, typically right under the add-to-cart button. You never edit theme code. Shopify's docs confirm merchants can "add, remove, and reorder app blocks at the section level" directly in the editor, and that app blocks "inherit styling properties from the theme, such as typography and colors," so the table matches your fonts and design without custom CSS.

The important structural advantage is that a well-built app block reads its tier values from the same source of truth as the discount, so the display and the checkout math cannot drift. That is the whole game, and it is the reason this route wins for most stores.

App blocks vs app embed blocks: which one is the tier table?

Shopify's theme app extension framework has two kinds of blocks, and it is worth knowing which is which, because the tier table is one of them and not the other.

An app block uses target: section in its schema. It is a visible, positioned element that a merchant drops into a specific spot inside a section, and it can point at dynamic sources like the current product. Per Shopify's configuration docs, app blocks are added, removed, and reordered in the theme editor and can "point to a dynamic source to show data for different products as they display on the page." That last part is exactly what a tier table needs: it has to show the tiers for whatever product the page is rendering. So the tier table is an app block.

An app embed block uses target: head, compliance_head, or body. Shopify "renders and injects app embed blocks before the closing head and body tags," and they are meant for floating or overlaid elements like chat bubbles, badges, and analytics or SEO tags, not for content placed at a precise point in the layout. App embed blocks are deactivated by default and merchants turn them on under Theme settings, App embeds. A tier table is not an embed block, because it needs to live in a specific position in the product layout, not float over the page.

One caveat that belongs in every honest guide: app blocks are supported only in Online Store 2.0 themes, the ones that use JSON templates. Shopify's onboarding docs state that "app blocks are supported only in themes that contain JSON templates, also known as Online Store 2.0 themes." If you are on a much older vintage theme, the drag-and-drop block placement will not be available and you are back to a Liquid approach or a theme upgrade. Most stores on a modern Shopify theme are already on Online Store 2.0.

IMAGE PLACEHOLDER (Image 2): Adding the tier-table app block in the theme editor
Suggested visual: A 16:9 screenshot-style mockup of the Shopify theme editor. Left panel shows the section tree for a product template with an "Add block" affordance expanded, revealing an app's "Quantity break table" block option under an Apps heading. Center shows the live product preview with the tier table appearing under the add-to-cart button. Polaris-style admin chrome, violet accent on the selected block. Reference the docs/screenshot-manifest.md slot volume-discounts-step-3 to capture the real screen later.

Why hand-coded tier tables silently break (the collection trap)

Here is the failure mode that generates the most frustrated forum posts, and it is not a bug in your table markup. It is a mismatch between how you think the discount is scoped and how Shopify actually counts.

Say you build a hand-coded table on a dining chair product page: 3 to 5 chairs save 15 percent, 6 to 9 save 18 percent, 10 or more save 20 percent. You back it with a native Shopify discount scoped to your "Chairs" collection. The table looks correct. Then two things quietly go wrong.

First, native collection-scoped discounts count total units across every product in the collection, not units of the one chair on the page. So a shopper can trigger your "buy 3" tier by adding one of each of three different chairs, which is not what the table on any single chair page implies. The displayed table is written from the perspective of one product; the discount is doing collection math. They describe different offers.

Second, the discount's membership changes underneath you. The moment you add a new product to that collection, or the moment a product falls out of it, the set of items that count toward the threshold shifts. Merchants have flagged exactly this on the forums: adding a product to a collection silently changes the discount logic, and nothing warns you. Your hand-coded table still shows the old tiers. It is now describing a discount that no longer exists in the form the customer sees.

This is the core reason a tier table should not be a static artifact you maintain by hand. The table is a view of a discount. If the discount can change scope or membership without touching the table, the two will drift, and the drift is invisible until a customer or your own test order surfaces it. The fix is structural: display per-product tiers from the same definition the discount uses, and count the way the table claims to count, per product and its variants, not pooled across an entire collection. That is the counting model covered in per-product volume discounts, and it is the difference between a table that is always true and one that is true until Tuesday.

The SEO trap: never render prices as heading tags

This one is a genuine, documented landmine, and it comes straight from the app reviews. A merchant flagged that a popular discount app rendered its on-page prices inside heading tags, which created multiple competing H1 elements on every product page, and support declined to fix it. That is not a cosmetic complaint. It is an SEO regression baked into the widget.

Here is why it matters. Search engines use your heading hierarchy to understand a page. The H1 is meant to be the single, primary title of the page, almost always the product name. The rest of the headings, H2 and H3, describe the structure beneath it. When a discount widget wraps "6 for 34 dollars" in an

or

because that was a lazy way to make the number big and bold, it injects headings that mean nothing semantically and dilute the ones that do. Multiple H1s on a page muddy the signal about what the page is actually about. Prices and "you save" figures are data, not document structure. They belong in table cells, spans, or paragraphs styled with CSS, never in heading elements.

The right way to render a tier table is boringly correct HTML: a real

(or a styled list) whose cells hold the quantities and prices, with visual weight applied through CSS, not through heading tags. The numbers can look as large and bold as your designer wants. They just must not pretend to be page headings. Shopify's own guidance for storefront widgets in the theme app extension framework points the same direction, and internally the rule we hold ourselves to is simple: storefront widgets must be SEO-safe, with no injected heading tags and no layout shift.

If you are evaluating a discount app, this is a concrete thing to check before you install. View source on a demo product page, or run it through your browser's accessibility or SEO inspector, and confirm the widget does not add heading elements. It is a five-minute test that saves you from a slow, hard-to-diagnose ranking decline. Marketing the absence of this problem is fair game, because so many widgets get it wrong.

Core Web Vitals: a tier table should not cost you speed

A product page tier table is small, but it renders on your highest-intent, highest-traffic pages, so its performance cost compounds. Two Core Web Vitals metrics are the ones to watch.

Cumulative Layout Shift (CLS) is the first. A table that pops in a moment after the rest of the page has painted shoves the add-to-cart button down and racks up layout shift. The fix is to render the table server-side in the initial HTML wherever possible, so the space is reserved from the first paint, rather than injecting it late with JavaScript after the page settles. A tier table's values are known at render time; there is no reason it needs to arrive last.

Largest Contentful Paint (LCP) and the general weight budget are the second. A widget that loads a heavy JavaScript bundle, its own web font, or a chunk of framework code to draw what is fundamentally a small table is spending your speed budget carelessly. Shopify's theme app extension framework helps here: when an app block is present on a page, its stylesheet and script are loaded once via the framework's own tags, and if a merchant adds multiple blocks that reference the same file, Shopify includes that file only once per page. A good tier table leans on that, ships minimal CSS and JS, and does the heavy lifting server-side.

The live-update behavior deserves a note too. A nice tier table highlights the active tier as the shopper changes quantity, with no page reload. That interaction is cheap when it is a few lines of vanilla JavaScript toggling a class, and expensive when it is a framework re-render. Keep it light. The point of the table is to sell more units, and a page that is slow to interact with sells fewer.

Worked example: a "quantity, price each, you save" table

Let us build a concrete one. A supplements brand sells a protein tub at 40 dollars. They want to reward stocking up on the same tub, counted per product across its flavors, with these tiers:

  • 1 to 2 units: full price
  • 3 to 5 units: 10 percent off
  • 6 to 11 units: 15 percent off
  • 12 or more units: 20 percent off

The table on the product page, placed directly under the add-to-cart button, reads like this. The "you save" column is what does the persuading.

A shopper who adds 6 tubs sees the "6 to 11" row highlight as their active tier, at 34 dollars each, saving 48 dollars against buying six at full price. The row above and below stay visible, so the next tier ("just six more and it drops to 32 each") is always in view. That visible next step is the mechanism. It is the same reason wholesale price grids have always been printed as grids.

The two rules from the rest of this guide both apply to this exact table. First, the values in the "price each" column must be the values the discount actually charges, counted per product across the tub's flavor variants, not pooled with unrelated products. Second, none of these cells may be a heading tag. The 34 dollars is a table cell styled to look prominent, not an

. Follow those two rules and this table is safe to ship on every product page in the catalog.

How to set it up, step by step

Here is the reliable sequence for the app-block route, which is the one most stores should use.

1. Confirm your theme is Online Store 2.0

App blocks require a JSON-template theme. If you are on a current Shopify theme, you almost certainly qualify. If you are on a vintage theme, plan a theme update first, or use a Liquid approach in the meantime.

2. Define the tiers once, in the discount

Set your quantity breaks in the discount itself: the breakpoints, the per-unit prices or percentages, and the counting mode (count this product's variants together, not the whole collection). This is the single source of truth. Everything the customer sees should derive from here.

3. Add the tier-table app block in the theme editor

Open your product template in the theme editor, choose Add block inside the product information section, and select the app's tier-table block. Drag it directly under the add-to-cart button. Because app blocks inherit the theme's typography and colors, it should match your design immediately; adjust only spacing or alignment if the block exposes those settings.

4. Verify the display equals the checkout math

Add the product to a real test cart at a quantity that crosses a tier boundary, for example 6 units, and confirm the price the table showed is the price at checkout, including through an accelerated checkout like Shop Pay. This is the step that catches drift before customers do.

5. Check the heading structure and layout stability

View the product page source and confirm the widget added no heading tags and did not push the add-to-cart button around as it loaded. A quick pass in an SEO or accessibility inspector confirms one H1, the product title, with the tier table living in a table or list, not in headings.

6. Test the collection edge case

If your discount is scoped, add and remove a product from the relevant collection or group and re-check that the tier table on your target product still shows the tiers you intend. This is where hand-maintained tables fail; a properly per-product-scoped offer does not.

IMAGE PLACEHOLDER (Image 3): The display and the checkout agree
Suggested visual: A 16:9 split illustration. Left panel labeled "Product page" shows the tier table with the 6-unit row highlighted at $34.00 each. Right panel labeled "Checkout" shows the same 6 units charged at $34.00 each with a green checkmark badge reading "Same price." A single unbroken chain icon links the two panels. Below, a caption strip reads "One source of truth: display equals checkout." Brand colors violet and gold, flat vector, no photography.

Run this reliably with Stackable

If you would rather not maintain a hand-coded table that drifts from your discount, or audit a widget's HTML for stray heading tags, this is the specific problem Stackable was built to handle, honestly and within Shopify's real rules.

  • The storefront tier table is a theme app block you drop under the add-to-cart button from the theme editor. No theme code, and it inherits your theme's fonts and colors so it looks native.
  • The table reads its tiers from the same discount definition the checkout uses, and counts per product and its chosen variants rather than pooling a whole collection, so what the shopper sees is what checkout charges. Change a collection's contents and the per-product table stays true.
  • It renders SEO-safe: prices sit in a real table, never in heading tags, so you keep one H1 (your product title) and do not create the competing-headings problem that has drawn app-review complaints elsewhere.
  • The table highlights the active tier live as the quantity changes, with server-computed values and no layout shift, so it stays inside your Core Web Vitals budget.
  • Because Stackable never rewrites your product prices, the tiers exist only as checkout adjustments. Uninstall and your catalog and theme are exactly as they were, with the block cleanly removed.

Install Stackable free and see the product-page table and the checkout total agree to the cent at usestackable.com/pricing.

The bottom line

  • A quantity break exists at checkout with or without a display, but a discount the customer cannot see cannot grow the cart. The product-page tier table is the merchandising half of the offer.
  • You can hand-code the table in Liquid, drive it from a metafield, or add a theme app block in the theme editor. The app block is the no-code route and, when built well, stays in sync with the discount automatically.
  • The tier table is an app block (target: section), not an app embed block, because it needs to sit at a precise spot in the product layout and show data for the current product.
  • Hand-coded tables drift. Native collection-scoped discounts count across the whole collection and change when you add or remove products, so a static table quietly starts describing an offer that no longer exists.
  • Never render prices in heading tags. That creates competing H1s and damages SEO, a real, documented complaint about some discount widgets. Put prices in table cells styled with CSS.
  • Watch Core Web Vitals: render server-side to avoid layout shift, keep the JavaScript light, and rely on the framework loading shared assets once per page.
  • The non-negotiable test is that the displayed price equals the checkout price, including through Shop Pay. Verify it with a real test order across a tier boundary before you launch.

Frequently Asked Questions

Find answers to common questions

  • Add a theme app block from a discount app in the theme editor, positioning it under the add-to-cart button, or hand-code the table in Liquid inside your product template. The app-block route needs no code and, in a well-built app, reads the same tier values the checkout uses. The essential requirement either way is that the displayed prices match what checkout charges and that the table does not use heading tags.

  • Yes, if your theme is Online Store 2.0 and you use a discount app that ships a theme app block. Shopify's theme editor lets you add, remove, and reorder app blocks visually, with no theme code. Hand-coding a table in Liquid does need developer comfort, and it adds ongoing maintenance because the table is then separate from the discount and must be kept in sync manually.

  • Only if it is built badly. The real danger is a widget that renders prices inside heading tags, which creates extra H1 or H2 elements and dilutes your page's heading structure. A correctly built table puts prices in table cells or spans styled with CSS, keeping a single H1 (your product title). Check any app by viewing source on a demo page and confirming it adds no heading elements.

  • Search engines read your heading hierarchy to understand a page. The H1 should be the page's primary title, usually the product name. When a widget wraps a price in an H1 or H2 to make it look big, it injects headings that carry no real meaning and create multiple competing headings, muddying the signal about what the page is about. Prices are data; style them with CSS, not heading tags.

  • It must, and this is the most important thing to verify. If the table is a hand-coded copy of your pricing, it can drift from the actual discount. If it reads from the same definition the discount engine uses, it cannot. Always place a test order at a quantity that crosses a tier boundary and confirm the product-page price equals the checkout price, including through Shop Pay.

  • With a native collection-scoped discount, adding or removing products changes which items count toward the threshold, and a hand-coded table will not reflect that. Merchants report this exact silent break on the forums. A per-product offer that counts the specific product and its variants, rather than pooling a collection, avoids the problem, so the table on that product page stays accurate regardless of collection changes.

  • They do not have to. Shopify's framework loads an app block's CSS and JavaScript once per page, even if multiple blocks share the file. A good tier table renders server-side so its space is reserved from first paint (protecting Cumulative Layout Shift), ships minimal CSS and JS, and uses a few lines of vanilla JavaScript for the live tier highlight rather than a heavy framework re-render.

  • An app block. App blocks use

Stackable Team

The team building Stackable, the reliability-first bulk and volume discount app for Shopify. We write about discount stacking, Shopify Functions, and how to run promotions that hold up at checkout.

We use essential cookies to run this site, and, only with your permission, analytics cookies to understand traffic. Read our Cookie Policy.