How to Add a Hot Stock Feature on Your Shopify Product Details Page
avatarSonya
02-26-2026 10:15 AM

In ecommerce, timing matters. When customers feel that a product might sell out soon, they are far more likely to complete their purchase instead of postponing the decision. A simple inventory reminder can significantly increase conversions.

In this article, we will walk you through how to add a Hot Stock feature to your product details page in the Shopify Horizon theme. This feature dynamically displays a low inventory message when stock drops below a set threshold, helping you create urgency in a clean and professional way.

This solution is especially suitable for merchants who want more flexibility than basic theme settings provide.

What Is the Hot Stock Function

The Hot Stock Function is a dynamic block that appears on the product page when inventory falls below a defined limit.

For example, when only a few items remain, customers might see messages such as:

Selling fast! Only 3 remaining

or

Last chance: 9 remaining!

The message automatically hides when inventory exceeds your threshold or when the product is out of stock. Everything is controlled through theme settings, so you can adjust appearance, spacing, and limits without editing code again.

How the Logic Works

Behind the scenes, the feature checks the selected variant inventory and compares it to a maximum stock limit defined in the block settings.

If:

Inventory > 0
and
Inventory ≤ Maximum threshold

The alert will display. Otherwise, it remains hidden.

When customers switch variants, the message updates automatically using JavaScript, ensuring accuracy at all times.

Customization Options Available

You can control:

  • Display style: text only or text with progress bar

  • Maximum inventory threshold

  • Font size and font weight

  • Text color

  • Block height

  • Top and bottom spacing

This makes the feature adaptable to almost any store design.


Step by Step Implementation in Shopify Horizon Theme

Before making any code modifications, always ensure you create a copy of the theme first. Test all your changes on this copy to confirm everything works perfectly before publishing it to the live theme. This precaution helps prevent any issues from affecting your main theme and keeps your work safe.

Go to:

Online Store → Themes → ... → Edit code

Step 1: Create the Liquid Block

Under Blocks, click New File and name it:

product-hot-stock.liquid

Then paste the following code into the file.

{%- liquid
    assign hot_stock_style = block.settings.hot_stock_style
    assign spacing_top = block.settings.spacing_top
    assign spacing_bottom = block.settings.spacing_bottom
    assign hot_stock_text_color = block.settings.hot_stock_color
    assign hot_stock_height = block.settings.hot_stock_height
    assign hot_stock_font_weight = block.settings.hot_stock_font_weight
    assign hot_stock_font_size = block.settings.hot_stock_font_size
    assign product_max_stock = block.settings.hot_stock_limit
    assign product_resource = closest.product
    assign current_variant = product_resource.selected_or_first_available_variant
    assign product_resource_id = product_resource.id
    assign inventory_qty = current_variant.inventory_quantity | plus: 0
    assign max_stock = product_max_stock | plus: 0
    if inventory_qty > 0 and inventory_qty <= max_stock
        assign show_hot_stock = true
    else
        assign show_hot_stock = false
    endif
-%}
<product-hot-stock data-product-id="{{ product_resource_id }}">
    <div
        class="hot-stock {% unless show_hot_stock %} is-hide {% endunless %}"
        style="--spacing-top: {{ spacing_top | append: 'px' }}; --spacing-bottom: {{ spacing_bottom | append: 'px' }}; --hot-stock-text-color: {{ hot_stock_text_color }}; --hot-stock-height: {{ hot_stock_height | append: 'px' }}; --hot-stock-font-weight: {{ hot_stock_font_weight }}; --hot-stock-font-size: {{ hot_stock_font_size | append: 'px' }}"
        data-hot-inventory-stock={{ inventory_qty }}
        data-hot-max-stock="{{ product_max_stock }}"
        data-hot-type="{{ hot_stock_style }}"
    >
        {%- if hot_stock_style == '2' -%}
            <span class="hot-stock__text">
                Last chance: {{ current_variant.inventory_quantity }} remaining!
            </span>
            <div class="hot-stock__progress">
                <span class="hot-stock__progress-item"></span>
            </div>
        {%- else -%}
            <span class="hot-stock__text">
                Selling fast! Only {{ current_variant.inventory_quantity }} remaining
            </span>
        {%- endif -%}
    </div>
</product-hot-stock>
{% stylesheet %}
.hot-stock {
    padding-top: var(--spacing-top);
    padding-bottom: var(--spacing-bottom);
    opacity: 1;
    visibility: visible;
    transform: scale(1);
    height: var(--hot-stock-height);
    transition: opacity 350ms ease, visibility 350ms ease, transform 350ms ease, height 350ms;
    will-change: transform;
}
.hot-stock.is-hide {
    opacity: 0;
    visibility: hidden;
    transform: scale(1, 0);
    height: 0;
}
.hot-stock__text {
    font-size: var(--hot-stock-font-size);
    font-weight: var(--hot-stock-font-weight);
    color: var(--hot-stock-text-color);
    margin: 6px 0; 
}
.hot-stock__progress {
    max-width: 300px;
    height: 5px;
    background-color: #f8f8f8;
    text-align: left;
    margin: 8px 0 0;
    display: block;
    overflow: hidden;
}
.hot-stock__progress-item {
    width: 100%;
    height: 100%;
    background: linear-gradient(45deg, #fd2d2d, #22c1c3);
    display: block;
    transition: all ease 1s;
}
{% endstylesheet %}
{% schema %}
{
  "name": "Hot Stock Function",
  "tag": null,
  "settings": [
    {
        "type": "select",
        "id": "hot_stock_style",
        "label": "Select Style",
        "default": "1",
        "options": [
            {
                "value": "1",
                "label": "1"
            },
            {
                "value": "2",
                "label": "2"
            }
        ]
    },
    {
        "type": "header",
        "content": "Spacing"
    },
    {
        "type": "range",
        "id": "spacing_top",
        "label": "Top",
        "min": 0,
        "max": 100,
        "step": 1,
        "unit": "px",
        "default": 0
    },
    {
        "type": "range",
        "id": "spacing_bottom",
        "label": "Bottom",
        "min": 0,
        "max": 100,
        "step": 1,
        "unit": "px",
        "default": 0
    },
    {
        "type": "header",
        "content": "Size"
    },
    {
        "type": "number",
        "id": "hot_stock_limit",
        "label": "The Maximum Inventory",
        "default": 20
    },
    {
        "type": "number",
        "id": "hot_stock_height",
        "label": "Hot Stock Height",
        "default": 35
    },
    {
        "type": "header",
        "content": "Appearance"
    },
    {
        "type": "range",
        "id": "hot_stock_font_size",
        "min": 12,
        "max": 24,
        "step": 1,
        "unit": "px",
        "label": "Font Size",
        "default": 14
    },
    {
        "type": "color",
        "id": "hot_stock_color",
        "label": "Font Color",
        "default": "#fd2d2d"
    },
    {
        "type": "select",
        "id": "hot_stock_font_weight",
        "label": "Font Weight",
        "default": "400",
        "options": [
          {
            "value": "400",
            "label": "Normal"
          },
          {
            "value": "500",
            "label": "Medium"
          },
          {
            "value": "600",
            "label": "Semi Bold"
          },
          {
            "value": "700",
            "label": "Bold"
          },
          {
            "value": "800",
            "label": "Bolder"
          },
          {
            "value": "900",
            "label": "Black"
          }
        ]
    }
  ],
  "presets": [
    {
      "name": "Hot Stock Function",
      "category": "t:categories.product"
    }
  ]
}
{% endschema %}

Step 2: Create the JavaScript File

Under Assets, click New File and name it:

product-hot-stock.js

Then paste the following code.

import { ThemeEvents, VariantUpdateEvent } from '@theme/events';
/**
 * A custom element that displays a hot stock.
 * This component listens for variant update events and updates the hot stock display accordingly.
 * It handles hot stock updates from two different sources:
 * 1. Variant picker in quick add modal or product page
 * 2. Swatches variant picker in product cards
 */
class ProductHotStock extends HTMLElement {
  connectedCallback() {
    const closestSection = this.closest('.shopify-section, dialog');
    if (!closestSection) return;
    closestSection.addEventListener(ThemeEvents.variantUpdate, this.updateStock);
    this.#initStock();
  }
  disconnectedCallback() {
    const closestSection = this.closest('.shopify-section, dialog');
    if (!closestSection) return;
    closestSection.removeEventListener(ThemeEvents.variantUpdate, this.updateStock);
  }
  /**
   * Init the hot stock.
   */
  #initStock = () => {
    const currentStockElement = this.querySelector('.hot-stock');
    if (!currentStockElement) return;
    const maxStock = parseFloat(currentStockElement.dataset.hotMaxStock);
    const hotStockType = currentStockElement.dataset.hotType;
    const inventoryQuantity = parseFloat(currentStockElement.dataset.hotInventoryStock); 
    if (hotStockType == 2) {
        const hotStockProgressItemElement = currentStockElement.querySelector('.hot-stock__progress-item');
        const inventoryProgress = inventoryQuantity / maxStock * 100;
        hotStockProgressItemElement.style.width = `${inventoryProgress}%`;
    }
  }
  /**
   * Updates the hot stock.
   * @param {VariantUpdateEvent} event
   */
  updateStock = (event) => {
    if (event.detail.data.newProduct) {
      this.dataset.productId = event.detail.data.newProduct.id;
    } else if (event.target instanceof HTMLElement && event.target.dataset.productId !== this.dataset.productId) {
      return;
    }
    const newStockText = event.detail.data.html.querySelector('.hot-stock__text');
    const currentStockText = this.querySelector('.hot-stock__text');
    if (!newStockText || !currentStockText) return;
    if (currentStockText.innerHTML !== newStockText.innerHTML) {
      currentStockText.replaceWith(newStockText);
    } 
    const newStockElement = event.detail.data.html.querySelector('.hot-stock');
    if (!newStockElement) return;
    const newInventoryQuantity = parseFloat(newStockElement.dataset.hotInventoryStock); 
    const currentStockElement = this.querySelector('.hot-stock');
    if (!currentStockElement) return;
    const maxStock = parseFloat(currentStockElement.dataset.hotMaxStock);
    if (newInventoryQuantity > 0 && newInventoryQuantity <= maxStock) {
        currentStockElement.classList.remove('is-hide');
    } else {
        currentStockElement.classList.add('is-hide');
    }
    const hotStockType = currentStockElement.dataset.hotType;
    if (hotStockType == 2) {
        const hotStockProgressItemElement = currentStockElement.querySelector('.hot-stock__progress-item');
        if (!hotStockProgressItemElement) return;
        const inventoryProgress = newInventoryQuantity / maxStock * 100;
        hotStockProgressItemElement.style.width = `${inventoryProgress}%`;
    }
  };
}
if (!customElements.get('product-hot-stock')) {
  customElements.define('product-hot-stock', ProductHotStock);
}

Step 3: Load the Script

Go to the Snippets folder and open scripts.liquid.

Paste the following code inside:

<script
  src="{{ 'product-hot-stock.js' | asset_url }}"
  type="module"
  fetchpriority="low"
></script>

Step 4: Enable the Block in Theme Editor

Go to:

Online Store → Themes → Customize

Open the Default product page.

Under the price section:

Click Add block → Select Hot Stock Function → Adjust the settings → Click Save → Preview

The hot stock message will now appear under the price when inventory is below your defined limit.

For any questions or further assistance, please don't hesitate to reach out. Simply leave us a message, and we will respond to you as soon as possible. We're here to help and look forward to working with you!