• Relative Time

    Format a date as relative time (e.g. "3 hours ago") using the native Intl.RelativeTimeFormat API. No dependencies, handles pluralization and locales automatically.

    Usage

    function timeAgo(date) {
      const locale = document.documentElement.lang || undefined;
      const rtf = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
      const diff = (new Date(date) - new Date()) / 1000; // seconds, negative = past
    
      const units = [
        ["year", 31536000],
        ["month", 2592000],
        ["week", 604800],
        ["day", 86400],
        ["hour", 3600],
        ["minute", 60],
        ["second", 1],
      ];
    
      for (const [unit, secs] of units) {
        if (Math.abs(diff) >= secs || unit === "second") {
          return rtf.format(Math.round(diff / secs), unit);
        }
      }
    }

    Web Component

    Wrap the same logic in a custom element for drop-in use in plain HTML, no glue code required. It re-renders every second so long-lived pages stay accurate, and updates automatically if the datetime attribute changes.

    <relative-time datetime="2026-07-12T09:00:00.000Z"></relative-time>
    class RelativeTime extends HTMLElement {
      static observedAttributes = ["datetime"];
    
      connectedCallback() {
        this.render();
        this.interval = setInterval(() => this.render(), 1000);
      }
    
      disconnectedCallback() {
        clearInterval(this.interval);
      }
    
      attributeChangedCallback() {
        this.render();
      }
    
      render() {
        const datetime = this.getAttribute("datetime");
        if (!datetime) return;
    
        const locale = document.documentElement.lang || undefined;
        const rtf = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
        const diff = (new Date(datetime) - new Date()) / 1000;
    
        const units = [
          ["year", 31536000],
          ["month", 2592000],
          ["week", 604800],
          ["day", 86400],
          ["hour", 3600],
          ["minute", 60],
          ["second", 1],
        ];
    
        for (const [unit, secs] of units) {
          if (Math.abs(diff) >= secs || unit === "second") {
            this.textContent = rtf.format(Math.round(diff / secs), unit);
            break;
          }
        }
      }
    }
    
    if (!customElements.get("relative-time")) {
      customElements.define("relative-time", RelativeTime);
    }

    Search 5021 icons

    Type a name to find icons from the Tabler icon set.