Free Countdown Timer Bar Section for ShopifyNo app. Copy, paste, done.
- Online Store 2.0
- No app
- Theme Check: 0 errors
- MIT license
The Stackframes countdown timer bar is a free Shopify section that counts down to a real sale end date you set, in your store's time zone. When the time runs out it hides itself or shows your "sale ended" message. It never restarts for each visitor, which keeps the urgency honest. Paste it into your theme, set the date, and it runs with a few lines of JavaScript and no app.
Live preview with sample content. Once installed, fonts follow your theme and colours are editable in the theme editor.
{%- liquid
assign now_s = 'now' | date: '%s' | plus: 0
assign end_s = 0
assign end_iso = ''
assign end_time = section.settings.end_time | strip
if end_time == blank
assign end_time = '23:59'
endif
assign time_parts = end_time | split: ':'
if time_parts.first.size == 1
assign end_time = '0' | append: end_time
endif
if section.settings.end_date != blank
assign end_iso = section.settings.end_date | strip | append: 'T' | append: end_time | append: ':00' | append: section.settings.utc_offset
assign end_s = end_iso | date: '%s' | plus: 0
endif
assign is_valid = false
if end_s > 1000000000
assign is_valid = true
endif
assign remaining = end_s | minus: now_s
assign show_timer = false
if is_valid and remaining > 0
assign show_timer = true
endif
assign show_expired = false
if is_valid and show_timer == false and section.settings.after_end == 'message'
assign show_expired = true
endif
assign d = 0
assign h = 0
assign m = 0
assign s = 0
if show_timer
assign d = remaining | divided_by: 86400 | floor
if section.settings.show_days
assign h = remaining | modulo: 86400 | divided_by: 3600 | floor
else
assign h = remaining | divided_by: 3600 | floor
endif
assign m = remaining | modulo: 3600 | divided_by: 60 | floor
assign s = remaining | modulo: 60
endif
-%}
{%- if show_timer or show_expired or request.design_mode -%}
<sf-countdown
class="sf-countdown"
data-end="{% if is_valid %}{{ end_s }}{% endif %}"
data-after="{{ section.settings.after_end }}"
data-days="{% if section.settings.show_days %}true{% else %}false{% endif %}"
style="--sf-countdown-bg: {{ section.settings.background_color }}; --sf-countdown-text: {{ section.settings.text_color }}; --sf-countdown-accent: {{ section.settings.accent_color }}; --sf-countdown-py: {{ section.settings.padding }}px;"
>
{%- if show_timer -%}
<div class="sf-countdown__live">
{%- if section.settings.message != blank -%}
<p class="sf-countdown__message">{{ section.settings.message | escape }}</p>
{%- endif -%}
<p class="sf-countdown__timer" aria-hidden="true">
{%- if section.settings.show_days -%}
<span class="sf-countdown__unit"><span class="sf-countdown__value" data-unit="days">{% if d < 10 %}0{% endif %}{{ d }}</span><span class="sf-countdown__label">days</span></span>
{%- endif -%}
<span class="sf-countdown__unit"><span class="sf-countdown__value" data-unit="hours">{% if h < 10 %}0{% endif %}{{ h }}</span><span class="sf-countdown__label">hrs</span></span>
<span class="sf-countdown__unit"><span class="sf-countdown__value" data-unit="minutes">{% if m < 10 %}0{% endif %}{{ m }}</span><span class="sf-countdown__label">min</span></span>
<span class="sf-countdown__unit"><span class="sf-countdown__value" data-unit="seconds">{% if s < 10 %}0{% endif %}{{ s }}</span><span class="sf-countdown__label">sec</span></span>
</p>
<span class="sf-countdown__sr">Offer ends {{ end_iso | date: '%B %d, %Y at %H:%M' }} (UTC{{ section.settings.utc_offset }}).</span>
{%- if section.settings.link != blank and section.settings.link_label != blank -%}
<a class="sf-countdown__link" href="{{ section.settings.link }}">{{ section.settings.link_label | escape }}</a>
{%- endif -%}
</div>
{%- endif -%}
{%- if section.settings.after_end == 'message' and is_valid -%}
<p class="sf-countdown__expired" {% unless show_expired %}hidden{% endunless %}>{{ section.settings.expired_message | escape }}</p>
{%- endif -%}
{%- if request.design_mode -%}
{%- if is_valid == false -%}
<p class="sf-countdown__empty">Enter the end date as YYYY-MM-DD and the time as HH:MM to start the countdown.</p>
{%- elsif show_timer == false and show_expired == false -%}
<p class="sf-countdown__empty">This countdown has ended, so it is hidden on your store. Set a new end date to show it again.</p>
{%- endif -%}
{%- endif -%}
</sf-countdown>
{%- endif -%}
{% stylesheet %}
.sf-countdown { display: block; background: var(--sf-countdown-bg); color: var(--sf-countdown-text); padding: var(--sf-countdown-py) 16px; text-align: center; }
.sf-countdown[hidden], .sf-countdown__live[hidden], .sf-countdown__expired[hidden] { display: none; }
.sf-countdown__live { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 8px 20px; }
.sf-countdown__message { margin: 0; font-weight: 600; }
.sf-countdown__timer { display: flex; gap: 8px; margin: 0; font-variant-numeric: tabular-nums; }
.sf-countdown__unit { display: inline-flex; align-items: baseline; gap: 3px; }
.sf-countdown__value { min-width: 2ch; font-size: 1.125em; font-weight: 700; color: var(--sf-countdown-accent); }
.sf-countdown__label { font-size: 0.75em; opacity: 0.8; }
.sf-countdown__link { display: inline-flex; align-items: center; min-height: 44px; padding: 0 4px; color: inherit; font-weight: 600; text-decoration: underline; text-underline-offset: 3px; }
.sf-countdown__link:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; border-radius: 2px; }
.sf-countdown__expired { margin: 0; font-weight: 600; }
.sf-countdown__sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.sf-countdown__empty { margin: 8px auto 0; max-width: 640px; padding: 12px 16px; border: 1px dashed currentColor; border-radius: 8px; font-size: 0.875em; opacity: 0.8; }
{% endstylesheet %}
{% javascript %}
if (!customElements.get('sf-countdown')) {
class SfCountdown extends HTMLElement {
connectedCallback() {
this.end = Number(this.dataset.end) * 1000;
if (!this.end) return;
this.tick();
if (!this.done) this.timer = window.setInterval(() => this.tick(), 1000);
}
disconnectedCallback() {
if (this.timer) window.clearInterval(this.timer);
this.timer = null;
}
tick() {
const left = Math.max(0, Math.floor((this.end - Date.now()) / 1000));
const parts = {
days: Math.floor(left / 86400),
hours: this.dataset.days === 'false' ? Math.floor(left / 3600) : Math.floor((left % 86400) / 3600),
minutes: Math.floor((left % 3600) / 60),
seconds: left % 60,
};
this.querySelectorAll('[data-unit]').forEach((el) => {
el.textContent = String(parts[el.dataset.unit]).padStart(2, '0');
});
if (left === 0) this.finish();
}
finish() {
this.done = true;
if (this.timer) window.clearInterval(this.timer);
this.timer = null;
if (this.dataset.after === 'message') {
const live = this.querySelector('.sf-countdown__live');
const expired = this.querySelector('.sf-countdown__expired');
if (live) live.hidden = true;
if (expired) expired.hidden = false;
} else if (!(window.Shopify && window.Shopify.designMode)) {
this.hidden = true;
}
}
}
customElements.define('sf-countdown', SfCountdown);
}
{% endjavascript %}
{% schema %}
{
"name": "SF countdown bar",
"tag": "section",
"class": "sf-countdown-section",
"settings": [
{ "type": "text", "id": "message", "label": "Message", "default": "Summer sale ends in" },
{ "type": "text", "id": "end_date", "label": "End date", "info": "Format YYYY-MM-DD, e.g. 2026-12-31. Leave empty to hide the bar." },
{ "type": "text", "id": "end_time", "label": "End time", "info": "24-hour format HH:MM, e.g. 09:00 or 23:59.", "default": "23:59" },
{
"type": "select",
"id": "utc_offset",
"label": "Time zone (UTC offset)",
"info": "Use your store's time zone so the sale ends at the right moment.",
"options": [
{ "value": "-12:00", "label": "UTC−12" },
{ "value": "-11:00", "label": "UTC−11" },
{ "value": "-10:00", "label": "UTC−10 (Hawaii)" },
{ "value": "-09:00", "label": "UTC−9 (Alaska)" },
{ "value": "-08:00", "label": "UTC−8 (Pacific)" },
{ "value": "-07:00", "label": "UTC−7 (Mountain)" },
{ "value": "-06:00", "label": "UTC−6 (Central)" },
{ "value": "-05:00", "label": "UTC−5 (Eastern)" },
{ "value": "-04:00", "label": "UTC−4 (Atlantic)" },
{ "value": "-03:30", "label": "UTC−3:30 (Newfoundland)" },
{ "value": "-03:00", "label": "UTC−3 (São Paulo, Buenos Aires)" },
{ "value": "-02:00", "label": "UTC−2" },
{ "value": "-01:00", "label": "UTC−1 (Azores)" },
{ "value": "+00:00", "label": "UTC (London winter, Lisbon winter)" },
{ "value": "+01:00", "label": "UTC+1 (London summer, Central Europe winter, Lagos)" },
{ "value": "+02:00", "label": "UTC+2 (Central Europe summer, Cairo, Johannesburg)" },
{ "value": "+03:00", "label": "UTC+3 (Istanbul, Moscow, Riyadh)" },
{ "value": "+03:30", "label": "UTC+3:30 (Tehran)" },
{ "value": "+04:00", "label": "UTC+4 (Dubai)" },
{ "value": "+04:30", "label": "UTC+4:30 (Kabul)" },
{ "value": "+05:00", "label": "UTC+5 (Karachi)" },
{ "value": "+05:30", "label": "UTC+5:30 (India)" },
{ "value": "+05:45", "label": "UTC+5:45 (Nepal)" },
{ "value": "+06:00", "label": "UTC+6 (Dhaka)" },
{ "value": "+06:30", "label": "UTC+6:30 (Yangon)" },
{ "value": "+07:00", "label": "UTC+7 (Bangkok, Jakarta)" },
{ "value": "+08:00", "label": "UTC+8 (Singapore, Perth)" },
{ "value": "+09:00", "label": "UTC+9 (Tokyo, Seoul)" },
{ "value": "+09:30", "label": "UTC+9:30 (Adelaide winter, Darwin)" },
{ "value": "+10:00", "label": "UTC+10 (Sydney winter, Brisbane)" },
{ "value": "+10:30", "label": "UTC+10:30 (Adelaide summer)" },
{ "value": "+11:00", "label": "UTC+11 (Sydney summer)" },
{ "value": "+12:00", "label": "UTC+12 (Auckland winter)" },
{ "value": "+13:00", "label": "UTC+13 (Auckland summer)" },
{ "value": "+14:00", "label": "UTC+14" }
],
"default": "+00:00"
},
{ "type": "checkbox", "id": "show_days", "label": "Show days", "default": true },
{ "type": "text", "id": "link_label", "label": "Link label", "default": "Shop the sale" },
{ "type": "url", "id": "link", "label": "Link" },
{
"type": "select",
"id": "after_end",
"label": "When the countdown ends",
"options": [
{ "value": "hide", "label": "Hide the bar" },
{ "value": "message", "label": "Show a message" }
],
"default": "hide"
},
{ "type": "text", "id": "expired_message", "label": "Ended message", "default": "This sale has ended. Thanks for shopping with us." },
{ "type": "header", "content": "Colors" },
{ "type": "color", "id": "background_color", "label": "Background", "default": "#111827" },
{ "type": "color", "id": "text_color", "label": "Text", "default": "#ffffff" },
{ "type": "color", "id": "accent_color", "label": "Numbers", "default": "#fcd34d" },
{ "type": "range", "id": "padding", "label": "Vertical padding", "min": 4, "max": 32, "step": 2, "unit": "px", "default": 10 }
],
"presets": [{ "name": "SF countdown bar" }]
}
{% endschema %}
- Ends at a real date and time in your store's time zone
- Hides itself, or shows a message, when the sale ends
- Never resets per visitor — no fake urgency
- Screen readers hear the end date, not a ticking clock
- Tiny script, no app, no external libraries
Add the Countdown Timer Bar to your theme
About two minutes. No app, no monthly fee.
Prefer a walkthrough? See the full installation guide.
- STEP 01
Duplicate your theme
In Shopify admin go to Online Store → Themes. Next to your current theme click ⋯ → Duplicate. Make every change on the copy.
- STEP 02
Create the section file
On the copy, click ⋯ → Edit code. In the sections folder click “Add a new section”, choose Liquid, name it sf-countdown-timer-bar and create it. Delete the starter code, paste ours, then click Save.
- STEP 03
Add it to a page
Click Customize, open the template you want, click “Add section” and choose “SF countdown bar”. Edit the text, blocks and colours in the sidebar, then Save.
- STEP 04
Check it, then publish
Preview on mobile and desktop. When you are happy, publish the duplicate theme.
What you can change in the theme editor
| Setting | Type | Default |
|---|---|---|
| Message | Text | Summer sale ends in |
| End dateFormat YYYY-MM-DD, e.g. 2026-12-31. Leave empty to hide the bar. | Text | — |
| End time24-hour format HH:MM, e.g. 09:00 or 23:59. | Text | 23:59 |
| Time zone (UTC offset)Use your store's time zone so the sale ends at the right moment. | Dropdown | UTC (London winter, Lisbon winter) |
| Show days | Checkbox | On |
| Link label | Text | Shop the sale |
| Link | Link | — |
| When the countdown ends | Dropdown | Hide the bar |
| Ended message | Text | This sale has ended. Thanks for shopping with us. |
| Background | Colour | #111827 |
| Text | Colour | #ffffff |
| Numbers | Colour | #fcd34d |
| Vertical padding | Slider | 10px |
This section is one piece of a custom theme.
We design and build complete Shopify themes around your brand.
Common questions
Does the timer reset for each visitor?
No. It counts down to the single end date and time you set, the same for every shopper. Timers that restart for each visitor are misleading, so this one never does.
What happens when the countdown ends?
You choose: hide the bar, or replace it with a message such as "This sale has ended." Pages rendered after the end date don't show the timer at all.
Which time zone does it use?
The UTC offset you pick in the settings. Choose your store's offset, and remember to update it if daylight saving time changes during the sale.
Where should I put the bar?
Most stores add it above the header or at the top of the home and collection pages. Add it to any template in the theme editor.
More free sections
Announcement Marquee
Free scrolling announcement bar for Shopify with multiple messages, links, a pause button and reduced-motion support. Paste the Liquid, no app.
Shipping Progress Bar
Free shipping progress bar for Shopify that shows how much more to spend, updates when the cart changes, and hides for other currencies. No app.
Trust Badges
Free trust badge section for Shopify: shipping, returns, secure checkout and support icons. Lightweight inline SVG, no JavaScript, no app.
All free Shopify sections
Get a Shopify theme built around your brand
Custom sections, a full theme build, or changes to the theme you already have. Tell us what your store needs and we'll scope it with you.
Not affiliated with or endorsed by Shopify Inc. Shopify is a trademark of Shopify Inc.