- 8 min read

How to Add a Shopify Countdown Timer Without Using an App (Shopify Dawn Theme Example)

In this comprehensive tutorial, I will show you how to add a Shopify Countdown Timer to your store using the Shopify Dawn Theme.

How to Add a Shopify Countdown Timer Without Using an App (Shopify Dawn Theme Example)
On this page
Introduction

Shopify countdown timers are a great tool to help you make more money with your Shopify store, so it makes sense that there are a lot of Shopify Apps that help you add one.

But honestly it doesn't make any sense to pay every month for a Shopify App to help you do this so in this tutorial I'll be showing you how you can add a Countdown Timer to your Shopify store in less than 10 minutes!

Now before we get started I just want to point out that these instructions will work the best if you are:

  • using the Shopify Dawn Theme on Version 11 or later
  • using a Shopify 2.0 theme that uses he Section Groups Feature

If you're not using one of the 2 themes mentioned above, you can follow this alternative tutorial instead.

Code For Countdown Timer Top Bar

{% comment %}
Liquid Variables
{% endcomment %}

{% liquid
assign timer_section_title = section.settings.timer_section_title
assign timer_section_subtitle = section.settings.timer_section_subtitle

assign date_picker_time = section.settings.date_picker

assign cta_bg_color = section.settings.cta_bg_color
assign cta_text_color = section.settings.cta_text_color

assign title_text_color = section.settings.title_text_color
assign subtitle_text_color = section.settings.subtitle_text_color
assign subtitle_font_weight = section.settings.subtitle_font_weight

assign section_bg_color = section.settings.bg_color
assign timer_color = section.settings.timer_color
%}



{% comment %}
Custom CSS Styles
{% endcomment %}
{% style %}
    .timer-flex-container{
        background: {{ section_bg_color }};
        align-items: center;
        justify-content: center;
        column-gap: 20px;
        padding: 10px;
        display: flex;
        opacity: 1;
        transition: 0.5s all linear;
    }

    .hide-element{
        padding: 0 !important;
        opacity: 0;
    }

    .timer-flex-container .timer {
        display: flex;
        justify-content: space-around;
    }

    .timer-flex-container .timer-subtitle{
        text-align:center; margin: 5px 0;
    }

    .timer-flex-container .timer-subtitle{
        color: {{ subtitle_text_color }};
        font-weight: {{ subtitle_font_weight }};
        font-family: inherit;
        font-size: 18px;
    }

    .timer-flex-container .timer--expired {
        display: none;
    }

    .timer-flex-container .timer__title {
        text-align: center;
        font-weight: bold;
    }

    .timer-flex-container .timer-display {
        display: -webkit-box;
        display: -ms-flexbox;
        display: flex;
        -webkit-box-pack: justify;
        -ms-flex-pack: justify;
        justify-content: center;
        margin-top: 5px;
    }

    .timer-flex-container .timer{
        margin-right: 1rem;
    }

    .timer-flex-container .timer-block {
        position: relative;
        padding: 0 10px;
    }

    .timer-flex-container .timer-block__num,
    .timer-flex-container .timer-block__unit {
        display: block;
        text-align: center;
        color: {{ timer_color }};
        font-family: inherit;
    }

    .timer-flex-container .timer-block__num{
        font-size: 16px;
    }

    .timer-flex-container .timer-block__unit{
        font-size: 13px;
        margin-top: -7px;
    }

    .timer-flex-container .colon {
        font-size: 16px;
        color: {{ timer_color }};
    }

    .timer-flex-container .timer-button{
        max-height: 45px;
        background-color: {{ section.settings.cta_bg_color }} !important;
        color: {{ section.settings.cta_text_color }} !important;
        border-radius: 4px;
    }

    .timer-flex-container .timer-button::after{
        content: none;
    }

    .timer-display-flex{
        display: flex;
        align-items: center;
    }

    @media screen and (max-width: 768px){
        .timer-flex-container{
            flex-direction: column;
        }
    }

    @media screen and (max-width: 391px){
        .timer-display-flex{
            width: 100%;
            justify-content: space-evenly;
            flex-wrap: wrap;
        }
    }

{% endstyle %}

{% unless date_picker_time == blank %}
<div class="timer-flex-container hide-element">
    {% unless timer_section_title == blank %}
        <div class="timer-subtitle">{{ timer_section_title }}</div>
    {% endunless %}


    <div class = "timer-display-flex">
        <countdown-timer-bar schema-time="{{ date_picker_time }}">
            <div class="timer" style="visibility: hidden;">
                <div class="timer-display">
                    <div class="timer-block">
                        <span class="timer-block__num js-timer-days">00</span>
                        <span class="timer-block__unit">Days</span>
                    </div>

                    <div class="colon">:</div>

                    <div class="timer-block">
                        <span class="timer-block__num js-timer-hours">00</span>
                        <span class="timer-block__unit">Hrs</span>
                    </div>

                    <div class="colon">:</div>

                    <div class="timer-block">
                        <span class="timer-block__num js-timer-minutes">00</span>
                        <span class="timer-block__unit">Mins</span>
                    </div>

                    <div class="colon">:</div>

                    <div class="timer-block">
                        <span class="timer-block__num js-timer-seconds">00</span>
                        <span class="timer-block__unit">Secs</span>
                    </div>
                </div>
            </div>
        </countdown-timer-bar>

        {% unless section.settings.timer_section_btn_link == blank and timer_section_btn_text == blank %}
        <a class="timer-button button" href="{{ section.settings.timer_section_btn_link }}">
            <span>{{
                section.settings.timer_section_btn_text }}
            </span>
        </a>
        {% endunless %}
    </div>

    
</div>
{% endunless %}


{% comment %}
Native Web Component Definition
{% endcomment %}
{% javascript %}
class CountdownTimerBar extends HTMLElement {
    constructor() {
        super();
    }

    init() {
        this.schemaTime = this.getAttribute('schema-time');

        this.timer = this.querySelector(".timer")
        this.countdown = new Date(this.schemaTime).getTime();
        this.second = 1000;
        this.minute = this.second * 60;
        this.hour = this.minute * 60;
        this.day = this.hour * 24;

        this.timerInterval = null;
        this.startTimer();

        setTimeout(() => this.timer.style.visibility = "visible", 1000)
    }

    padWithLeadingZero = (number) => {
        if (number < 10) {
            return '0' + number;
        } else {
            return number;
        }
    };

    isDateInPast(date) {
        const now = new Date();
        return date < now;
    }


    startTimer() {
        //Have quick check to see if Date input in the countdown class variable is in the past
        //Helper function

        const timerElem = this.timer
        this.timerInterval = setInterval(() => {
            const now = new Date().getTime();
            const distance = this.countdown - now;

            const dayCalc = Math.floor(distance / this.day);
            const hourCalc = Math.floor((distance % this.day) / this.hour);
            const minuteCalc = Math.floor((distance % this.hour) / this.minute);
            const secondCalc = Math.floor((distance % this.minute) / this.second);

            timerElem.querySelector('.js-timer-days').textContent = this.padWithLeadingZero(dayCalc);
            timerElem.querySelector('.js-timer-hours').textContent = this.padWithLeadingZero(hourCalc);
            timerElem.querySelector('.js-timer-minutes').textContent = this.padWithLeadingZero(minuteCalc);
            timerElem.querySelector('.js-timer-seconds').textContent = this.padWithLeadingZero(secondCalc);
        }, this.second);
    }

    disconnectedCallback() {
        clearInterval(this.timerInterval);
    }

    connectedCallback(){
        this.init();
        console.log("countdown timer was added to the page")
        
        const timerFlexContainer = window.document.querySelector(".timer-flex-container")
        const timerElem = this.timer

        const pastDate = this.isDateInPast(this.countdown)
        if(pastDate){
            timerFlexContainer.remove()
            this.remove()
        }
        window.setTimeout(() => {
            timerFlexContainer.classList.remove("hide-element")
        }, 2000);
    }

    disconnectedCallback(){
        console.log("The Timer date has passed so the timer was removed")
    }
}

customElements.define('countdown-timer-bar', CountdownTimerBar);
{% endjavascript %}


    {% comment %}
    Section Customization Options
    {% endcomment %}
    {% schema %}
    {
        "name": "Countdown Top Bar",
        "tag": "section",
        "enabled_on": {
            "groups": ["header"]
        },
        "settings": [
            {
                "type": "header",
                "content": "General Section Settings"
            },
            {
                "type": "color",
                "id": "bg_color",
                "default": "#fff",
                "label": "Section Background Color"
            },
            {
                "type": "header",
                "content": "Title Settings"
            },
            {
                "type": "text",
                "id": "timer_section_title",
                "label": "Section Title",
                "default": "Hurry up! Sale ends in:"
            },
            {
                "type": "color",
                "id": "subtitle_text_color",
                "default": "#000",
                "label": "Section Title Text Color"
            },
            {
                "type": "select",
                "id": "subtitle_font_weight",
                "default": "500",
                "label": "Normal or Bolded Text",
                "options": [
                    {
                        "label": "Normal",
                        "value": "500"
                    },
                    {
                        "label": "Bold",
                        "value": "700"
                    }
                ]
            },
            {
                "type": "header",
                "content": "Timer Settings"
            },
            {
                "type": "text",
                "id": "date_picker",
                "label": "Choose a date",
                "info": "Input date in this format: 07/21/2023. When date is in past section will disappear"
            },
            {
                "type": "color",
                "id": "timer_color",
                "default": "#000",
                "label": "Timer Color"
            },
            {
                "type": "header",
                "content": "Button Settings"
            },
            {
                "type": "url",
                "id": "timer_section_btn_link",
                "label": "Call to Action Button Link"
            },
            {
                "type": "text",
                "id": "timer_section_btn_text",
                "label": "Call to Action Button Text",
                "default": "Shop Now!"
            },
            {
                "type": "color",
                "id": "cta_bg_color",
                "default": "#4770db",
                "label": "Call to Action Background Color"
            },
            {
                "type": "color",
                "id": "cta_text_color",
                "default": "#fff",
                "label": "Call to Action Text Color"
            }
        ],
        "presets": [
            {
                "name": "Countdown Top Bar"
            }
        ]
    }
    {% endschema %}

Code For Countdown Timer Banner

{% comment %}
    Liquid Variables
{% endcomment %}

{% liquid
    assign timer_section_title = section.settings.timer_section_title
    assign timer_section_subtitle = section.settings.timer_section_subtitle

    assign date_picker_time = section.settings.date_picker
    assign timer_end_message = section.settings.timer_end_message

    assign cta_bg_color = section.settings.cta_bg_color
    assign cta_text_color = section.settings.cta_text_color

    assign title_text_color = section.settings.title_text_color
    assign subtitle_text_color = section.settings.subtitle_text_color

    assign section_bg_color = section.settings.bg_color
    assign timer_color = section.settings.timer_color

    assign timer_title_size = section.settings.timer_title_size
    assign timer_subtitle_size = section.settings.timer_subtitle_size
%}



{% comment %}
    Custom CSS Styles
{% endcomment %}
{%  style  %} 
    .section-padding{
        padding: 40px 0;
        background: {{ section_bg_color }};
    }

    .timer {
        justify-content: space-around;
        padding-bottom: 10px;
    }

    .timer-title, .timer-subtitle{
        text-align:center; margin: 5px 0;
    }

    .timer-title{
        color: {{ title_text_color }}
    }

    .timer-subtitle{
        color: {{ subtitle_text_color }}
    }

    .timer--expired {
        display: none;
    }

    .timer__title {
        text-align: center;
    }

    .timer-display {
        display: -webkit-box;
        display: -ms-flexbox;
        display: flex;
        -webkit-box-pack: justify;
        -ms-flex-pack: justify;
        justify-content: center;
        margin-top: 5px;
    }

    .timer-block {
        position: relative;
        padding: 0 10px;
    }

    .timer-block__num,
    .timer-block__unit {
        display: block;
        text-align: center;
        color: {{ timer_color }};
    }

    .timer-block__num {
        font-size: 30px;
    }

    .timer-button{
        max-height: 45px;
        background-color: {{ cta_bg_color }} !important;
        color: {{ cta_text_color }} !important;
        border-radius: 4px;
    }

    .timer-button::after{
        content: none;
    }

    .colon {
        font-size: 30px;
        color: {{ timer_color }};
    }
{% endstyle %}

{% unless timer_section_title == blank or timer_section_subtitle == blank %}
    <div style = "margin: 10px 0">
        <h2 class = "timer-title" style = "font-size: {{ timer_title_size }}px">{{ timer_section_title }}</h2>
        <p class = "timer-subtitle" style = "font-size: {{ timer_subtitle_size }}px">{{ timer_section_subtitle }}</p>
    </div>
{% endunless %}

{% unless date_picker_time == blank %}
    <countdown-timer schema-time = "{{ date_picker_time }}">
        <div class="timer" style = "visibility: hidden;">
            <div class="timer-display">
                <div class="timer-block">
                    <span class="timer-block__num js-timer-days">00</span>
                    <span class="timer-block__unit">Days</span>
                </div>
        
                <div class="colon">:</div>
        
                <div class="timer-block">
                    <span class="timer-block__num js-timer-hours">00</span>
                    <span class="timer-block__unit">Hrs</span>
                </div>
        
                <div class="colon">:</div>
        
                <div class="timer-block">
                    <span class="timer-block__num js-timer-minutes">00</span>
                    <span class="timer-block__unit">Mins</span>
                </div>
        
                <div class="colon">:</div>
        
                <div class="timer-block">
                    <span class="timer-block__num js-timer-seconds">00</span>
                    <span class="timer-block__unit">Secs</span>
                </div>
            </div>
        </div>
    </countdown-timer>
{% endunless %}

{% unless section.settings.timer_section_btn_link == blank and timer_section_btn_text == blank %}
    <div style = "text-align:center">
        <a class = "button timer-button" href="{{ section.settings.timer_section_btn_link }}">
         {{ section.settings.timer_section_btn_text }}
        </a>
    </div>
{% endunless %}


{% comment %}
    Native Web Component Definition
{% endcomment %}
{% javascript %}
    class CountdownTimer extends HTMLElement {
    constructor() {
        super();
    }

    init() {
        this.schemaTime = this.getAttribute('schema-time');
        this.timer = this.querySelector(".timer")
        this.countdown = new Date(this.schemaTime).getTime();
        this.second = 1000;
        this.minute = this.second * 60;
        this.hour = this.minute * 60;
        this.day = this.hour * 24;

        this.timerInterval = null;
        this.startTimer();

        setTimeout(() => this.timer.style.visibility = "visible", 1000)
    }

    padWithLeadingZero = (number) => {
        if (number < 10) {
            return '0' + number;
        } else {
            return number;
        }
    };

    isDateInPast(date) {
        const now = new Date();
        return date < now;
    }


    startTimer() {
        //Have quick check to see if Date input in the countdown class variable is in the past
        //Helper function

        const timerElem = this.timer

        this.timerInterval = setInterval(() => {
            const now = new Date().getTime();
            const distance = this.countdown - now;

            const dayCalc = Math.floor(distance / this.day);
            const hourCalc = Math.floor((distance % this.day) / this.hour);
            const minuteCalc = Math.floor((distance % this.hour) / this.minute);
            const secondCalc = Math.floor((distance % this.minute) / this.second);

            timerElem.querySelector('.js-timer-days').textContent = this.padWithLeadingZero(dayCalc);
            timerElem.querySelector('.js-timer-hours').textContent = this.padWithLeadingZero(hourCalc);
            timerElem.querySelector('.js-timer-minutes').textContent = this.padWithLeadingZero(minuteCalc);
            timerElem.querySelector('.js-timer-seconds').textContent = this.padWithLeadingZero(secondCalc);
        }, this.second);
    }

    disconnectedCallback() {
        clearInterval(this.timerInterval);
    }

    connectedCallback(){
        this.init();
        console.log("countdown timer was added to the page")
        
        const timerFlexContainer = window.document.querySelector(".timer-flex-container")
        const timerElem = this.timer

        const pastDate = this.isDateInPast(this.countdown)
        if(pastDate){
            timerFlexContainer.remove()
            this.remove()
        }
        window.setTimeout(() => {
            timerFlexContainer.classList.remove("hide-element")
        }, 2000);
    }
}


    customElements.define('countdown-timer', CountdownTimer);
{% endjavascript %}


{% comment %}
    Section Customization Options
{% endcomment %}
{% schema %}
    {
        "name": "Countdown Timer Banner",
        "tag": "section",
        "disabled_on": {
            "groups": ["header", "footer"]
        },
        "class": "section-padding",
        "settings": [
            {
                "type": "header",
                "content": "General Section Settings"
            },
            {
                "type": "color",
                "id": "bg_color",
                "default": "#fff",
                "label": "Section Background Color"
            },
            {
                "type": "header",
                "content": "Title Settings"
            },
            {
                "type": "text",
                "id": "timer_section_title",
                "label": "Section Title",
                "default": "Hurry Up!"
            },
            {
                "type": "text",
                "id": "timer_section_subtitle",
                "label": "Section Sub Title",
                "default": "Sale ends in:"
            },
            {
                "type": "color",
                "id": "title_text_color",
                "default": "#000",
                "label": "Title Text Color"
            },
            {
                "type": "color",
                "id": "subtitle_text_color",
                "default": "#000",
                "label": "Subtitle Text Color"
            },
            {
                "type": "range",
                "id": "timer_title_size",
                "default": 24,
                "label": "Title Text Size",
                "min": 20,
                "max": 40
            },
            {
                "type": "range",
                "id": "timer_subtitle_size",
                "default": 16,
                "label": "Subtitle Text Size",
                "min": 15,
                "max": 30
            },
            {
                "type": "header",
                "content": "Timer Settings"
            },
            {
            "type": "text",
            "id": "date_picker",
            "label": "Choose a date",
            "info": "input date in this format: 07/21/2023"
            },
            {
                "type": "color",
                "id": "timer_color",
                "default": "#000",
                "label": "Timer Color"
            },
            {
                "type": "header",
                "content": "Button Settings"
            },
            {
                "type": "url",
                "id": "timer_section_btn_link",
                "label": "Call to Action Button Link"
            },
            {
                "type": "text",
                "id": "timer_section_btn_text",
                "label": "Call to Action Button Text",
                "default": "Shop Now!"
            },
            {
                "type": "color",
                "id": "cta_bg_color",
                "default": "#4770db",
                "label": "Call to Action Background Color"
            },
            {
                "type": "color",
                "id": "cta_text_color",
                "default": "#fff",
                "label": "Call to Action Text Color"
            }
        ],
        "presets": 
        [
            {
                "name": "Countdown Timer Banner"
            }
        ]
    }
{% endschema %}

Alternative Countdown Timer Tutorial