Shopify Estimated Delivery Date Snippet
A business-day "arrives by" line that accounts for your cutoff time and holidays.
Your details
Your snippet file (snippets/estimated-delivery.liquid)
{%- comment -%}
Estimated delivery date, calculated in the shopper's browser so it never goes stale on a cached page.
{%- endcomment -%}
{%- liquid
assign processing_days = 1
-%}
<ea-estimated-delivery
data-tz="America/New_York"
data-cutoff="14:00"
data-processing="{{ processing_days }}"
data-transit-min="3"
data-transit-max="5"
data-skip-weekends="true"
data-holidays="2026-11-26,2026-12-25,2027-01-01"
data-template="Get it by {date} if you order today"
></ea-estimated-delivery>
<script>
if (!customElements.get('ea-estimated-delivery')) {
customElements.define('ea-estimated-delivery', class extends HTMLElement {
connectedCallback() { this.render(); }
partsIn(date, tz) {
var map = {};
new Intl.DateTimeFormat('en-US', { timeZone: tz, hourCycle: 'h23', year: 'numeric', month: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric' }).formatToParts(date).forEach(function (p) { map[p.type] = p.value; });
return map;
}
render() {
var tz = this.dataset.tz;
var now;
try { now = this.partsIn(new Date(), tz); } catch (e) { now = this.partsIn(new Date(), 'UTC'); }
var cutoff = (this.dataset.cutoff || '14:00').split(':').map(Number);
var afterCutoff = Number(now.hour) > cutoff[0] || (Number(now.hour) === cutoff[0] && Number(now.minute) >= cutoff[1]);
var holidays = (this.dataset.holidays || '').split(',').filter(Boolean);
var processingDays = Number(this.dataset.processing) || 0;
var transitMin = Number(this.dataset.transitMin) || 0;
var transitMax = Number(this.dataset.transitMax) || 0;
var skipWeekends = this.dataset.skipWeekends === 'true';
var startDays = processingDays + (afterCutoff ? 1 : 0);
var base = new Date(Date.UTC(Number(now.year), Number(now.month) - 1, Number(now.day)));
function addBusinessDays(date, days) {
var d = new Date(date.getTime());
var added = 0;
var guard = 0;
while (added < days && guard < 400) {
d.setUTCDate(d.getUTCDate() + 1);
guard++;
var iso = d.toISOString().slice(0, 10);
var dow = d.getUTCDay();
if (skipWeekends && (dow === 0 || dow === 6)) continue;
if (holidays.indexOf(iso) !== -1) continue;
added++;
}
return d;
}
var minDate = addBusinessDays(base, startDays + transitMin);
var maxDate = addBusinessDays(base, startDays + transitMax);
var fmt = new Intl.DateTimeFormat(document.documentElement.lang || 'en-US', { weekday: 'short', month: 'short', day: 'numeric' });
var text = transitMin === transitMax ? fmt.format(maxDate) : fmt.format(minDate) + '–' + fmt.format(maxDate);
this.textContent = (this.dataset.template || 'Get it by {date}').replace('{date}', text);
}
});
}
</script>To install: in Shopify admin go to Online Store → Themes, click … next to your theme → Edit code. Under Snippets choose Add a new snippet, name it as shown above, paste this code and save. Add {% render 'estimated-delivery', product: product %} inside your product template, usually near the Buy buttons.
Label this clearly as an estimate. Carrier delays can still push delivery past this date.
Live preview
Why this runs in the visitor's browser
Shopify caches rendered product pages, so a date calculated when the page was last cached (using Liquid) would go stale, still showing yesterday's "arrives by" date to a shopper viewing the page today. This snippet works around that by calculating the date in the shopper's browser instead, every time the page loads, using your cutoff time, processing days, transit window and holiday list.
It skips weekends when you ask it to, skips any date in your holiday list, and compares the current time against your cutoff in the timezone you pick, not the shopper's own timezone, so the promise is accurate to your fulfillment schedule.
Adding it to a product page
- 01
Add the snippet
Online Store → Themes → Edit code → Snippets → Add a new snippet named
estimated-delivery, paste the code, then add{% render 'estimated-delivery', product: product %}inside your product template, usually near the Buy buttons.
Questions people ask
Can different products have different processing times?
Turn on the per-product metafield option, then set a custom.processing_days number metafield on any product that needs a different value than your default.
Does it account for the shopper's own timezone?
No, on purpose: your cutoff time is about when your warehouse stops processing same-day orders, which happens in your timezone, not the shopper's.
What happens over a holiday?
Any date you list is skipped entirely, both as a possible cutoff day and when counting transit days, and treated like a closed day.
Is this legally binding shipping information?
Label it clearly as an estimate (the default text does this) and keep your written shipping policy as the source of truth. Delivery estimates can still be affected by carrier delays outside your control.
Related tools
Last reviewed 30 September 2026.
