How to Add a Race Countdown Timer to Your Publii Website

I’m racing HYROX Anaheim on December 3rd, and I wanted a live countdown on my homepage. It sounded like a five-minute job. It took a few tries, because Publii has two quirks that quietly break most countdown snippets you find online. This guide skips the trial and error. You’ll get a working countdown timer for any event: a race, a launch, a wedding, a product drop.

What you’ll build

A dark card on your homepage showing the event name, the date and time, and a live days / hours / minutes / seconds countdown, with a button linking to the event page. When the timer hits zero, it switches to an “It’s race day!” message.

The whole thing is one script. No plugins, no external libraries, no theme edits.

Why most snippets fail in Publii

I hit both of these problems, and they’re the reason this guide exists.

1. The text editor strips scripts. If you paste a countdown into a post or a hero text field, Publii’s rich-text editor keeps the HTML box but removes the <script> tag. The box shows up, but the numbers sit at 0 forever. It can even look like it’s working in the editor, which is misleading.

2. The Body tab runs too early. In Custom HTML, the Body tab injects code near the top of the page, before your content exists. A script placed there looks for the countdown box, finds nothing, and quits silently.

The fix is to put the script in the Footer tab of Custom HTML and have it wait for the page to finish loading.

Step 1: Pick your anchor text

The script places the countdown right under a heading on your homepage. Decide which heading. In my case it’s the hero title, “Learn. Build. Share.” Any heading text that appears on your page will work, as long as it’s unique.

Step 2: Open Custom HTML

  1. In Publii, open your site.
  2. Go to Tools & Plugins → Custom HTML.
  3. Leave the Body tab empty.
  4. Open the Footer tab.

Step 3: Paste the script

Copy the script below into the Footer tab. Everything you need to change is in the five lines under CONFIGURATION.

<script>
document.addEventListener("DOMContentLoaded", function () {
  // ===== CONFIGURATION: edit these five lines =====
  var eventName  = "HYROX Anaheim 2026";
  var dateText   = "December 3, 2026 · 10:00 AM PST";
  var target     = new Date("2026-12-03T10:00:00-08:00").getTime();
  var eventUrl   = "https://usa.hyrox.com/events/hyrox-anaheim-season-26-27-edyxxn";
  var anchorText = "Learn. Build. Share.";
  // ================================================

  var heading = null;
  var all = document.querySelectorAll("h1,h2,h3,h4,h5,h6");
  for (var i = 0; i < all.length; i++) {
    if (all[i].textContent.indexOf(anchorText) !== -1) {
      heading = all[i];
      break;
    }
  }
  if (!heading) {
    console.log("Countdown: anchor heading not found");
    return;
  }

  var box = document.createElement("div");
  box.id = "event-countdown";
  box.style.cssText = "max-width:520px;margin:2rem auto;padding:1.5rem;border-radius:12px;background:#111;color:#fff;text-align:center;font-family:system-ui,sans-serif;";
  box.innerHTML =
    '<div style="margin:0 0 .25rem;color:#f7d117;font-size:1.4rem;font-weight:700;"></div>' +
    '<p style="margin:0 0 1rem;opacity:.8;"></p>' +
    '<div id="ec-timer" style="display:flex;gap:.75rem;justify-content:center;">' +
      '<div><span id="ec-d" style="font-size:2.2rem;font-weight:700;">0</span><br><small>days</small></div>' +
      '<div><span id="ec-h" style="font-size:2.2rem;font-weight:700;">0</span><br><small>hours</small></div>' +
      '<div><span id="ec-m" style="font-size:2.2rem;font-weight:700;">0</span><br><small>min</small></div>' +
      '<div><span id="ec-s" style="font-size:2.2rem;font-weight:700;">0</span><br><small>sec</small></div>' +
    '</div>' +
    '<a href="#" style="display:inline-block;margin-top:1rem;color:#f7d117;">Event details &rarr;</a>';

  var next = heading.nextElementSibling;
  var anchor = (next && next.tagName === "P") ? next : heading;
  anchor.insertAdjacentElement("afterend", box);

  box.children[0].textContent = eventName;
  box.children[1].textContent = dateText;
  box.querySelector("a").href = eventUrl;

  var d = document.getElementById("ec-d"),
      h = document.getElementById("ec-h"),
      m = document.getElementById("ec-m"),
      s = document.getElementById("ec-s"),
      timerBox = document.getElementById("ec-timer");

  function tick() {
    var diff = target - Date.now();
    if (diff <= 0) {
      timerBox.innerHTML = '<strong style="font-size:1.4rem;">It\'s race day!</strong>';
      clearInterval(timer);
      return;
    }
    d.textContent = Math.floor(diff / 86400000);
    h.textContent = Math.floor(diff / 3600000) % 24;
    m.textContent = Math.floor(diff / 60000) % 60;
    s.textContent = Math.floor(diff / 1000) % 60;
  }
  var timer = setInterval(tick, 1000);
  tick();
});
</script>

Step 4: Customize the five settings

SettingWhat it doesExample
eventNameTitle shown on the card“HYROX Anaheim 2026”
dateTextHuman-readable date line“December 3, 2026 · 10:00 AM PST”
targetThe actual countdown moment2026-12-03T10:00:00-08:00
eventUrlWhere the button linksYour event or ticket page
anchorTextHeading the card appears under“Learn. Build. Share.”

Getting the time zone right

The target value uses the format YYYY-MM-DDTHH:MM:SS followed by a UTC offset. The offset is what makes the countdown correct for every visitor, wherever they are.

  • Pacific Standard Time (winter): -08:00
  • Pacific Daylight Time (summer): -07:00
  • Eastern Standard Time: -05:00
  • Eastern Daylight Time: -04:00
  • UTC: Z instead of an offset

Use the offset that applies on the event date, not today’s date. My race is in December, so I use -08:00 even though I wrote this earlier in the year.

Step 5: Save, preview, and hard refresh

  1. Click Save & preview.
  2. When the preview opens, do a hard refresh: Ctrl+Shift+R on Windows and Linux, Cmd+Shift+R on Mac. Browsers cache aggressively, and an old version can hide your changes.

Your countdown should now appear under your anchor heading and tick down every second.

Troubleshooting

If nothing shows up, open your browser console (F12 → Console), reload the page, and look for these:

  • “Countdown: anchor heading not found”: the anchorText doesn’t match your page. Check for typos, trailing spaces, or special characters, or choose a different heading.
  • No message and no countdown: the script isn’t being output. Confirm it’s in the Footer tab (not Head or Body) and that you re-rendered the site with Save & preview.
  • Countdown appears but the time is wrong: check the UTC offset in target and make sure it’s the offset for the event date.
  • Two countdowns on one page: you probably also pasted a box into a text field or the Body tab. Remove the older copies.

Make it your own

A few easy tweaks:

  • Colors: change #111 (background) and #f7d117 (accent) in the script to match your brand.
  • Width: adjust max-width:520px to make the card narrower or wider.
  • Placement: change anchorText to a heading elsewhere on the page to move the card.
  • Finish message: edit the “It’s race day!” text to anything you like, such as “We’re live!” or “Happy wedding day!”.

Wrapping up

The lesson here goes beyond countdowns: in Publii, anything involving JavaScript belongs in the Footer tab of Custom HTML, not in a post or text field. Once you know that, small interactive touches like timers, banners, and widgets become easy to add.