xDiscount is on the Shopify App Store. Founding merchants lock in lifetime founder pricing.
Docs

Storefront

Storefront Offer Card

Add the xDiscount offer block to your theme so shoppers see each live offer, its conditions, its countdown and the gift picker before checkout.

What the Offer Card Is

Your discounts apply automatically at checkout on every plan, so shoppers never enter a code. The storefront offer card is the other half of that. It is a small card on your product page that tells shoppers about the offer before they reach checkout.

The card reads exactly the same configuration the checkout discount reads. That is deliberate. What a shopper is shown and what they are charged cannot drift apart.

Two things to know before you start:

  • The offer card is included on every plan, Free included. Your discounts apply automatically at checkout whether or not the card is on your theme, and the card only changes what shoppers see beforehand.
  • The card is a theme app block. It does not appear until you add it to your theme yourself, in the Shopify theme editor. Nothing about a discount changes when you add it.

Buy X get Y needs this card

A Shopify discount function cannot put an item into a cart, so the card’s gift picker is what adds the gift. xDiscount says so on the discount form: “Buy X get Y needs the storefront offer card installed on your theme. A discount cannot add an item to the cart on its own, so the card is what puts the gift there.”

Adding the Block to Your Theme

1

Open Settings

In xDiscount, open Settings and find the Storefront offer card section. It is the first one, and the Storefront offer card tab shows it on its own.

Check the badge

The badge next to the section tells you whether the block is already on your live theme. The three readings are in the table below.

Click Add to theme

Your Shopify theme editor opens in a new tab, on the product template, with the xDiscount offer block already placed in a new Apps section. This is the deep link Shopify documents for theme app extensions, so there is nothing to find in the block picker. If the block is not there, because of an older theme or a picker that did not take the link, click Add block and choose xDiscount offer yourself.

Place it and save

Drag the block where you want it. Just under your theme’s Add to cart button is a good place. Click Save in the theme editor.

Go back to xDiscount

The badge flips to On on its own within a few seconds. If it has not caught up, click Check again.

What the badge means:

BadgeWhat it means
OnThe block is on your live theme’s product template and switched on
OffThe block is not there, so follow the steps above
Couldn’t checkWe could not read your theme just now. This is not the same as “not installed”, so click Check again

Once the block is installed, the button changes to Open theme editor and opens the same template without adding a second copy of the block. An Uninstall button appears beside Check again. It opens the same editor, because Shopify gives apps no way to remove a block from a theme. The card says so above the button, and once you delete the block there, the badge updates on its own.

What the check actually looks at

  • Your live, published theme only. If you publish a different theme, add the block there too.
  • Your default product template, because the card is a product-page block. If some of your products use an alternate product template, the card still shows wherever you added the block, but the badge only reflects the default one.
  • A block that is present but switched off in the theme editor counts as not added.
  • A block nested inside a group or another block is still found.
  • A theme that predates Online Store 2.0 has no product template of this kind and cannot take app blocks at all, so the badge stays on Off.
  • The block is recognised by its own block handle, the offer_card in shopify://apps/…/blocks/offer_card/<uuid>. That segment is xDiscount’s own and does not change when the app is renamed or the extension re-registered, so the badge keeps working across upgrades.

Shopify never notifies an app when a theme block is added or removed, so after you click Add to theme xDiscount goes and looks for itself: once after two seconds, then every five seconds, up to twenty times, stopping as soon as the badge changes. After that, use Check again.

The same check on Home

The Home screen’s Getting started list has a matching step, “Show the offer on your product pages”, with an Open settings button. It runs the same theme check as Settings, so the two screens agree on what “added” means, and Home marks that step Couldn’t check when your theme cannot be read.

If a badge ever reads Not on your plan

The card is included on every plan, Free included, with the same badge and the same buttons everywhere. A badge reading Not on your plan can only mean your subscription sits on a plan the app does not recognise. Use Refresh plan under Plans & billing, and if it stays, tell us.

Theme Editor Settings

Select the xDiscount offer block in the theme editor. Its settings are grouped under six headers: Text, Stock line, Conditions, Colours, Size and spacing and Display.

Everything about what the card says about a discount still comes from the discount itself. None of these settings can change an amount, a condition or a date, so the card and checkout cannot disagree.

Text

SettingTypeDefaultWhat it does
HeadingText(empty)Optional. A heading above the offers. Leave empty for none, because each offer already carries its own name
SubheadingText(empty)Optional. A second line under the heading. Leave empty to show none
Details lineText(empty)The small line under each offer’s name. Leave empty for Click to see details
No conditions wordingText(empty)Shown in the window when an offer has no conditions. Leave empty for No special conditions required
Gift picker labelText(empty)The wording on the gift button and the title of the gift window. Leave empty for Choose your free gift

Every text setting is optional, and empty means the translated default. That is deliberate. A store in another language is never handed English from a default you did not type.

Stock line

SettingTypeDefaultWhat it does
Show stock on the offerCheckboxTickedShows how many of the selected variant are in stock on each offer, as Only 7 left in stock, and follows the variant the shopper selects. Nothing is shown at zero, or when stock is not tracked
Stock wordingText(empty)Your own wording, with {count} where the number goes. Leave empty for Only {count} left in stock

The number is your theme’s own stock figure at the moment the page renders. It is only ever shown, and no discount is decided from it.

Conditions

Three checkboxes decide which kinds of condition the window shows, and six optional templates under the same header decide the wording.

SettingTypeDefaultWhat it does
Show quantity conditionsCheckboxTickedWhether quantity conditions appear in the window
Show cart value conditionsCheckboxTickedWhether cart value conditions appear in the window
Show stock conditionsCheckboxTickedWhether inventory conditions appear in the window

Hiding a condition changes nothing at checkout

The discount still requires it, and shoppers are simply not told. If the only condition an offer has is one you hid, the window shows nothing at all rather than claiming there are no conditions, because that claim would be false and checkout would prove it.

Condition wording. Six optional templates, one per kind of condition, so the window speaks in your words. Each takes {min} and, where it applies, {max} where the numbers go. Leave a template empty for the default wording.

SettingFilled in for
Quantity, minimum only and minimum and maximumAn item quantity condition with one bound, or with both
Cart value, minimum only and minimum and maximumCart value conditions. {min} and {max} are filled in your store’s money format, for example $50.00, or 50 EUR on a foreign market, so write the template without a currency symbol
Stock, minimum only and minimum and maximumInventory conditions

A condition with Negate this condition ticked uses the same template, wrapped as Not: …. A maximum-only bound, and an older date condition, always use the default wording.

Why there is no template for negated conditions

Shopify allows an app block at most 25 settings and 6 headers, and this block uses all of both. That is why there is no second set of templates for negated conditions, and why the window’s Conditions heading is not a setting.

Colours

SettingTypeDefaultWhat it does
Urgency textColour(empty)The fill of each offer tile. Leave empty for the default violet
TextColour(empty)The colour written on the tiles. Leave empty and the card picks white or near-black to suit your tile colour, so a light tile never gets invisible white text
Card backgroundColour(empty)An optional background behind the whole block. Leave empty for none
BorderColour(empty)An optional border around the whole block. Leave empty for none

Each offer is a filled, rounded tile, and Urgency text is the colour it is filled with. Out of the box that is a violet, #8B5CF6, with white writing. The block itself has no background or border unless you give it one, so the tiles sit directly on your product page.

Size and spacing

SettingTypeDefaultWhat it does
Card widthSelectFull widthFull width, Small (240px), Medium (340px) or Large (400px). This caps each tile, not the block. On Full width the tiles share the row and grow to fill it
Corner radiusRange, 0 to 24px in steps of 212How rounded each tile is
PaddingRange, 0 to 32px in steps of 416The space between a tile’s edge and its contents

The offers are laid out as a responsive grid: as many tiles per row as fit at 280px each, wrapping onto the next row below that. A tile lifts slightly when a shopper points at it, and does not move at all for a shopper who has asked their system to reduce motion.

Display

SettingTypeDefaultWhat it does
Show countdownCheckboxTickedShows a live countdown under any discount with an end date, in your store’s timezone
Countdown segmentsSelectDays, hours, minutes, secondsWhich segments the countdown shows: Days, hours, minutes, seconds, Hours, minutes, seconds or Minutes, seconds. Days are hidden while there are none left, and hours while there are no hours or days

The countdown is a strip of Days / Hours / Min / Sec under the offer, and it counts to the end of the last day in your store’s timezone. The end moment is stamped with your store’s UTC offset, so a shopper in another country sees the same time remaining you do, not their own midnight. The strip turns amber under a day, and red, gently pulsing, under an hour. A shopper who has asked their system to reduce motion sees no pulse.

The countdown only ever describes a discount

Whether an offer appears at all is decided on the server, against your store’s day, by the same comparison checkout makes. A shopper whose device clock is wrong sees wrong digits and the right offers, never an offer checkout would refuse. The Ends today wording is printed on a discount’s final day whether this box is ticked or not.

Whatever you type into any text setting is shown to shoppers exactly as you wrote it, and is escaped before it reaches the page, so a stray quote or angle bracket in your wording can never break the card or your theme.

What Shoppers See

On a product page with at least one live offer, the card shows your Heading and Subheading if you set them, and then one tile per live discount, laid out as a grid of filled, rounded tiles.

Each tile reads, left to right: the discount’s name as you typed it on the discount form, the Details line under it, the stock line if you switched it on, and the value large on the right.

Discount typeWhat the value says
Percentage20% off, the number you set, always read between 0 and 100
Fixed amount$10.00 off, and see the currency section below
Buy X get YFree gift, or 50% off a gift for a partial gift discount
Free shippingFree shipping

Clicking a tile opens a window with the discount’s name, its value, and its conditions, in the same order and with the same joins as the discount actually evaluates them. If your discount needs 3 items and the card did not say so, a shopper would only find out at checkout. An offer with no conditions says so.

Condition you setWhat shoppers read
Item quantity, both boundsBuy 2 to 5
Item quantity, minimum onlyBuy 2 or more
Item quantity, maximum onlyUp to 5
Cart value, both boundsSpend $50.00 to $200.00
Cart value, minimum onlySpend $50.00 or more
Cart value, maximum onlySpend up to $200.00
Inventory, both boundsWhile stock is between 5 and 10
Inventory, minimum onlyWhile at least 5 are in stock
Inventory, maximum onlyWhile stock is 10 or fewer
An older date condition, both dates2026-09-01 to 2026-09-30
An older date condition, start onlyFrom 2026-09-01
An older date condition, end onlyUntil 2026-09-30
Negate this condition tickedNot: Buy 2 or more

Rows after the first carry their own joining word, and or or, on the line above them, matching the way conditions are read top to bottom on the discount form. A negated condition is marked in red. A condition the card does not recognise renders nothing at all rather than a guess, and your Condition wording templates replace the default phrases above wherever you set one.

An inventory line states your bound

An Inventory line prints your bound, never a live stock count. That condition is resolved by the app before checkout, per variant, so the card is explaining the rule rather than reporting today’s shelf. See Conditions & Rules for what that means when your stock moves during the day.

A name reaches the storefront when its product syncs

A discount saved before this version shows its tile without a name line until it is saved again, or until the daily sweep resyncs it.

Finally, on the last day of a discount that has an end date, the tile shows Ends today above its countdown.

When the Card Shows Nothing

The card renders nothing, so no empty box, no heading, not even its stylesheet, unless a product has at least one live offer to show. That means no card when:

  • the product is not targeted by any discount;
  • every discount on the product is Draft or Archived;
  • today falls outside every discount’s Discount period;
  • the block is not on your live theme.

Both period dates are inclusive whole days in your store’s timezone, and each side works independently. A start date with no end date runs forever, and a discount whose start and end are the same date runs for that one day.

An unreadable date is treated differently here

If a date on a discount is unreadable for any reason, the card treats it as not set and keeps showing the offer, while checkout does the opposite and stops applying it. That asymmetry is on purpose. A card that shows too often is visible and you can fix it, while a discount that wrongly applies costs you money on every order.

The card follows the variant the shopper selects. A discount narrowed to specific variants says Applies to selected variants only in its details while the selected variant is one of them. The moment the shopper picks a variant you left out, that offer is dimmed and flagged Not available for the selected variant, and it comes back when they pick one you included. A whole-product discount that skips out-of-stock variants, or carries an Inventory condition, does the same on the variants it skips, and the stock line follows too.

The card reads the selected variant the way every theme announces it — the page’s URL and the product form — so what a shopper sees on the tile is what checkout will do for the variant in their hands.

The Gift Picker, Buy X Get Y

On a Buy X get Y discount, the tile carries a button reading Choose your free gift, or whatever you typed into Gift picker label. It opens the gift window.

What shoppers see in the window. One row per gift product you picked, each with the product’s title, a small image where we have one, and an Add button. A gift with no title falls back to Free gift. Under the list, a line reads 0 of 2 selected, counting against the Number of gift items you set on the discount.

A gift with more than one variant to choose from reads Choose instead of Add: clicking it expands the variants, each named as your store names them (Small, Large) with an Add of its own, and a variant that is already sold out is greyed out as Sold out. Variant names and availability are read from your store’s own product data when the window opens, so they are always current and never stored on the discount.

What happens on Add. One unit of that gift is added to the cart, marked as a gift line, the row changes to Added, and the count goes up by one. A shopper who wants two of the same gift clicks Add twice, and one who wants one of each clicks each once. Your discount then takes the percentage you set off that line at checkout, for at most that many units across the whole cart. Once the count reaches the limit, every Add button greys out, because a further gift would be charged in full at checkout. A button is also greyed out while its request is in flight and comes back when it finishes, so a shopper who hit a failure can try again.

Things worth knowing:

  • The marker the card puts on the cart line says only “this line is a gift”. Every value, so the percentage, how many gifts are covered, the schedule and the conditions, still comes from your discount. A shopper’s browser can never decide what a gift costs.
  • Which variants a gift offers is your call in the picker: tick the product whole and every variant is offered, or open it and tick specific variants to limit the choice. Checkout only ever discounts a gift variant you allowed, whatever a shopper’s browser claims. A gift saved before this option existed offers the one variant captured at the time — open the picker and reselect it to offer more. A gift we have no variant for at all is skipped, and the whole gift area is hidden if that leaves nothing to choose.
  • After the gift is added, the card tells your theme to refresh its own cart. Most themes pick this up. If yours does not update its cart drawer, the item is still in the cart, and a page refresh will show it.
  • A Buy X get Y discount with no gift products cannot be saved as Active at all, and the discount form warns: “No gift chosen yet, shoppers cannot claim this offer until you pick one.”

Why Amounts Show in Your Store’s Currency

This is the question merchants ask most about the card, so here is the straight answer.

Fixed amounts are stored in your store’s currency, because that is the currency you typed them in. At checkout, Shopify converts that figure to the shopper’s currency using the real exchange rate for their market.

The card, running in your theme, has no access to that exchange rate. Shopify’s theme templates expose a currency’s code, name and symbol, never the rate. So if the card simply formatted your number with the shopper’s currency symbol, a shopper in a euro market would read “€10.00 off” while checkout took about €9.20. The card and checkout would disagree, which is exactly what this app is built to prevent.

The rule the card follows: format the amount normally unless a mismatch is possible.

SituationWhat the card prints
The shopper’s currency and your store’s currency are the same$10.00 off, your theme’s normal money formatting
The shopper is on a market with a different currency, or we cannot read theirs10 EUR off, your own figure, with your store’s currency code stated

The same rule applies to Cart value conditions, so a threshold never tells a shopper to spend the wrong amount to qualify.

Percentages are never affected, because 20% off is 20% off in every currency, and neither is free shipping. The caution costs you nothing except on the amounts where being wrong would actually matter.

Selling in one currency only

You will never see the second form. It appears only on multi-currency markets, and what it shows is true on every one of them: checkout converts your figure for the shopper.