Blog
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!
