> ## Documentation Index
> Fetch the complete documentation index at: https://developers.mageloyalty.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Bonus campaign countdown

> Show a bonus campaign badge and a live countdown on your product pages.

Show customers that a product earns bonus points or store credit right now, and how long they have left. This recipe renders two badges on the product page:

* A campaign pill, such as **2× points this weekend**
* A countdown, such as **Ends in 2d 14h**, that updates every minute

Both badges only appear while an active campaign covers the product, and disappear when it ends.

## 1. Add the markup

Place this where you want the badges to appear in your product template, for example above the product image or next to the price:

```html theme={null}
<div class="bonus-campaign" data-bonus-campaign hidden>
  <span class="bonus-campaign__pill" data-bonus-campaign-pill></span>
  <span class="bonus-campaign__timer" data-bonus-campaign-timer></span>
</div>
```

The `hidden` attribute keeps the badges out of sight until a campaign is found. If your theme CSS sets `display` on `.bonus-campaign`, add `[hidden] { display: none !important; }` so the attribute still wins.

## 2. Add the script

Add this to your product template (or your theme's JavaScript). Replace the `{{ product.id }}` Liquid tag if you render the product ID another way.

```html theme={null}
<script>
  (function () {
    const productId = {{ product.id }};
    const root = document.querySelector('[data-bonus-campaign]');
    if (!root) return;
    const pillEl = root.querySelector('[data-bonus-campaign-pill]');
    const timerEl = root.querySelector('[data-bonus-campaign-timer]');
    let timer = null;

    // "Ends in 2d 14h" / "Ends in 3h 20m" / "Ends in 12m", or null once it has ended
    function formatTimeLeft(endsAt) {
      const msLeft = new Date(endsAt) - Date.now();
      if (msLeft <= 0) return null;
      const days = Math.floor(msLeft / 86400000);
      const hours = Math.floor((msLeft % 86400000) / 3600000);
      const minutes = Math.floor((msLeft % 3600000) / 60000);
      if (days > 0) return `Ends in ${days}d ${hours}h`;
      if (hours > 0) return `Ends in ${hours}h ${minutes}m`;
      return `Ends in ${Math.max(minutes, 1)}m`;
    }

    function hide() {
      root.hidden = true;
      if (timer) clearInterval(timer);
    }

    function render(campaign, unit) {
      const timeLeft = formatTimeLeft(campaign.endsAt);
      if (!timeLeft) return hide();

      if (campaign.type === 'multiplier') {
        pillEl.textContent = `${campaign.multiplier}× ${unit} this weekend`;
      } else {
        // Spend-threshold campaign: show the lowest tier
        const tier = campaign.tiers[0];
        pillEl.textContent = `Spend ${tier.spendFormatted}, get ${tier.bonusFormatted} bonus`;
      }
      timerEl.textContent = timeLeft;
      root.hidden = false;

      // Keep the countdown ticking without calling the SDK again
      if (timer) clearInterval(timer);
      timer = setInterval(() => {
        const next = formatTimeLeft(campaign.endsAt);
        if (!next) return hide();
        timerEl.textContent = next;
      }, 60000);
    }

    async function init() {
      const campaignResp = await MageSDK.getProductBonusCampaign({ productId });
      if (!campaignResp.success || !campaignResp.data) return hide();

      // Pick the wording for the shop's loyalty mode
      const rulesResp = await MageSDK.getEarningRules();
      const unit = rulesResp.success && rulesResp.data.loyaltyMode === 'store_credit' ? 'store credit' : 'points';

      render(campaignResp.data, unit);
    }

    if (window.MageSDK) {
      init();
    } else {
      document.addEventListener('mage-sdk-loaded', init);
    }
  })();
</script>
```

Style the two elements to match your theme; the `bonus-campaign__pill` and `bonus-campaign__timer` classes are there for that.

## Showing the boosted amount

To add a line like "Earn 500 points with this purchase, usually 250", pair this recipe with [getProductEarningValue](/js-sdk/product/get-product-earning-value). Only show the "usually" wording when its `bonusApplied` is `true`.

## Notes

* The pill text is yours to write. `campaign.multiplier` gives you the number; the campaign `name` from the Mage admin is also available if you prefer to display that.
* The countdown is based on `endsAt`, which is a UTC timestamp, so it is correct for every customer regardless of their timezone.
* When the campaign has a required product (see `requiredProducts`), you may want to change the wording until it is in the cart, for example "Add the gift box to unlock 2× points".


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.