How to Add a Shipping Cutoff Timer to Shopify Without an App
A shipping cutoff timer tells shoppers exactly how long they have left to order and still get their package shipped that day. It's one of the few conversion elements that works on urgency and reduces support tickets ("did my order ship yet?"). Most apps that do this charge a monthly fee, and many inject scripts that slow down your storefront. You can build the same thing directly into your Shopify theme with about 60 lines of Liquid and JavaScript — no app, no recurring cost, full control.
Why a Shipping Cutoff Timer Boosts Conversions (and Why You Don't Need an App)
The psychology is simple: a deadline converts better than a vague promise. "Order within 3 hours 12 minutes for same-day dispatch" outperforms "Fast shipping!" because it's specific and time-bound. It also sets accurate expectations, which cuts down on "where is my order" emails from customers who ordered at 9pm expecting same-day handling.
You don't need an app for this because a countdown timer is fundamentally just:
- A target timestamp (your daily cutoff, adjusted for the customer's timezone)
- A
setIntervalloop that recalculates the remaining time - A conditional that hides the timer once the cutoff passes
Shopify apps that offer this typically bundle it with shipping rules, delivery-date estimates, or upsells — you're paying for the bundle, not the timer. If you only want the timer, theme code is leaner, faster, and free. It also won't break when an app updates or when you switch themes (though you'll need to re-add it after a theme change — see the testing section).
One honest caveat: this approach assumes a single daily cutoff time in a single timezone (your warehouse's). If you ship from multiple warehouses with different cutoffs per region, or you need per-product handling times, an app like a delivery-date-estimate tool is genuinely easier. For the majority of small stores with one fulfillment location, the code below is enough.
What You'll Need Before You Start Editing Theme Code
Before touching anything, get these in place:
- Theme access: Shopify admin → Online Store → Themes → Actions → Edit code. You need a theme that supports Liquid section/snippet editing. All free Shopify themes (Dawn, Refresh, Craft) and most paid ones (Impulse, Prestige, Warehouse) allow this.
- A theme backup: In the Themes page, click Actions → Duplicate before you edit. This gives you a rollback point in one click.
- Your cutoff details: the exact time (e.g., 2:00 PM), the timezone your warehouse operates in (e.g., America/New_York), and which days you ship (Mon–Fri? Mon–Sat?).
- A test product and a test order path: you'll want to preview on a real product page and in the cart.
| Item | Where to find it | Example value |
|---|---|---|
| Cutoff time | Your fulfillment schedule | 14:00 |
| Warehouse timezone | IANA timezone name | America/New_York |
| Shipping days | Your carrier pickup days | Mon–Fri |
| Theme file editor | Admin → Online Store → Themes → Edit code | — |
Use IANA timezone names (like America/New_York, Europe/London), not abbreviations like "EST" — abbreviations are ambiguous and break across daylight saving changes.
Step 1: Create the Countdown Snippet in Your Theme
In the theme code editor, open the snippets folder and click Add a new snippet. Name it shipping-cutoff-timer.liquid. Naming it as a snippet means you can render it from multiple templates (product, cart, and even a banner) without duplicating code.
Paste this starter structure:
{%- assign cutoff_hour = 14 -%}
{%- assign cutoff_minute = 0 -%}
{%- assign cutoff_tz = 'America/New_York' -%}
<div
id="shipping-cutoff-timer"
class="shipping-cutoff-timer"
data-cutoff-hour="{{ cutoff_hour }}"
data-cutoff-minute="{{ cutoff_minute }}"
data-cutoff-tz="{{ cutoff_tz }}"
hidden
>
<span class="shipping-cutoff-timer__label">Order within</span>
<span class="shipping-cutoff-timer__clock" data-timer-clock>--:--:--</span>
<span class="shipping-cutoff-timer__label">for same-day dispatch</span>
</div>
The hidden attribute keeps the timer invisible until JavaScript confirms it should show. This prevents a flash of "00:00:00" or a timer that appears after the cutoff has already passed.
Step 2: Add the Liquid Logic for Your Shipping Cutoff Time
The tricky part of any cutoff timer is timezones. Shopify renders Liquid on its servers, and date filters output in the store's configured timezone — not the customer's browser timezone. That mismatch is the #1 source of "my timer says 4 hours but it's already 3pm" bugs.
The clean fix: let JavaScript do the timezone math using the browser's Intl API, and use Liquid only to pass your cutoff settings into the DOM. That's what the data- attributes above do.
If you want a server-side fallback message (for customers with JavaScript disabled), add this just below the timer div:
<noscript>
<p class="shipping-cutoff-timer__fallback">
Orders placed before {{ cutoff_hour }}:{{ cutoff_minute }} on business days ship the same day.
</p>
</noscript>
You can also make the cutoff configurable per store without editing code, by moving the three assign lines into a theme setting. If you're comfortable with settings_schema.json, add a text setting for the timezone and a number setting for the hour. Otherwise, editing the snippet directly is fine — it's one file, and you'll rarely change it.
Render the snippet where you want it. In sections/main-product.liquid (or your theme's product template), add near the buy button:
{% render 'shipping-cutoff-timer' %}
Step 3: Write the JavaScript Countdown and Hide It After Cutoff
Add this to the bottom of the snippet, inside a <script> tag. It calculates the next cutoff moment in your warehouse timezone, compares it to the customer's current time, and updates every second.
<script>
(function () {
const el = document.getElementById('shipping-cutoff-timer');
if (!el) return;
const hour = parseInt(el.dataset.cutoffHour, 10);
const minute = parseInt(el.dataset.cutoffMinute, 10);
const tz = el.dataset.cutoffTz;
const clock = el.querySelector('[data-timer-clock]');
function nextCutoff() {
const now = new Date();
// Build "today at cutoff" in the warehouse timezone
const fmt = new Intl.DateTimeFormat('en-US', {
timeZone: tz, hour12: false,
year: 'numeric', month: '2-digit', day: '2-digit',
hour: '2-digit', minute: '2-digit', second: '2-digit'
});
const parts = Object.fromEntries(
fmt.formatToParts(now).map(p => [p.type, p.value])
);
let target = Date.UTC(
parts.year, parts.month - 1, parts.day, hour, minute, 0
);
// Offset between UTC and warehouse local time
const local = Date.UTC(
parts.year, parts.month - 1, parts.day,
parts.hour, parts.minute, parts.second
);
const offset = local - now.getTime();
target = target - offset;
if (target <= now.getTime()) target += 86400000;
return target;
}
function pad(n) { return String(n).padStart(2, '0'); }
function tick() {
const now = Date.now();
let diff = nextCutoff() - now;
if (diff <= 0) { el.hidden = true; return; }
const h = Math.floor(diff / 3600000);
const m = Math.floor((diff % 3600000) / 60000);
const s = Math.floor((diff % 60000) / 1000);
clock.textContent = h + ':' + pad(m) + ':' + pad(s);
el.hidden = false;
}
tick();
setInterval(tick, 1000);
})();
</script>
Two things this handles that naive timers don't:
- Daylight saving: because the offset is recalculated from the live
Intloutput on every tick, the timer self-corrects when clocks change. - Post-cutoff behavior: once the cutoff passes, the code rolls the target to the next day. If you don't ship on weekends, you'll want to extend
nextCutoff()to skip Saturday and Sunday — add a check onnew Date(target).getUTCDay()and push forward until it lands on a shipping day.
Step 4: Style the Timer and Place It on Product and Cart Pages
Add CSS to your theme's assets/base.css (or theme.css, depending on your theme):
.shipping-cutoff-timer {
display: flex;
align-items: center;
gap: 0.4rem;
font-size: 0.9rem;
font-weight: 600;
padding: 0.5rem 0.75rem;
border-radius: 6px;
background: #f3f7f4;
color: #1a5c3a;
margin: 0.75rem 0;
}
.shipping-cutoff-timer__clock {
font-variant-numeric: tabular-nums;
font-weight: 700;
}
tabular-nums keeps the digits from shifting width as the seconds change — without it, the text jitters every tick.
For placement, render the snippet in two spots:
- Product page: right below the Add to Cart button, so the deadline is visible at the moment of decision.
- Cart page: in
sections/main-cart.liquid, above the checkout button. This is where the timer earns its keep — a shopper hesitating at checkout is exactly who the deadline moves.
You can also add it to a cart drawer if your theme uses one. Find the drawer's Liquid file (often snippets/cart-drawer.liquid) and render the snippet there.
Testing Your Timer and Avoiding Common Timezone Mistakes
Test before you publish. Use your theme's Preview and check these cases:
- Before cutoff: the timer shows and counts down correctly.
- After cutoff: the timer hides entirely (or rolls to tomorrow, depending on your preference).
- Weekend behavior: if you don't ship Saturday/Sunday, confirm the timer points to Monday.
- Different timezones: change your computer's system timezone (or use browser dev tools' sensor emulation) to a timezone far from your warehouse and verify the countdown still ends at your warehouse's cutoff, not the browser's local time.
The most common mistake is using new Date() with a hardcoded offset like -05:00. That works until daylight saving flips and your timer is off by an hour for half the year. Always derive the offset from Intl.DateTimeFormat as shown above.
Second most common: rendering the timer with Liquid's date filter and assuming it reflects the customer's clock. It reflects the store's timezone setting, which may differ from both the customer and your warehouse.
Third: forgetting that theme updates overwrite your edits. When you update your theme, Shopify may replace files. Keep a copy of the snippet and the CSS block in a note, and re-add after major theme updates. Duplicating the theme before edits (from the prep section) makes rollback painless.
Conclusion
A shipping cutoff timer built into your theme costs nothing, loads faster than an app, and gives you complete control over the message and placement. The four steps above — snippet, Liquid settings, JavaScript countdown, and styling — take about 30 minutes for a single-location store. Test the timezone edge cases carefully, and the timer will quietly do its job on every product and cart page.
FAQ
Will this work if my store uses multiple currencies or languages? Yes. The timer is independent of currency and language settings. The only text is the label you write in the snippet, so translate it per market if you use Shopify Markets.
Can I show different cutoffs for different products? Not with this single-snippet approach. You'd need to pass a product-specific cutoff value into the snippet via a metafield. That's doable but adds complexity — at that point, a delivery-estimate app may be the better trade.
Does the timer keep counting if a customer leaves the tab open overnight?
Yes. The setInterval recalculates against the live clock every second, so when the cutoff passes the timer hides itself, and when the next day's cutoff window opens it reappears.