Playground
Shows a moment relative to now, like "3 minutes ago" or "in 2 days", with the browser's Intl.RelativeTimeFormat. It keeps itself current (one timer for every instance on the page, each updated only when its text changes). Hovering shows the full date.
Put a server-formatted date inside it: that is what shows before the component loads, for search engines, and without JavaScript.
Examples
Live
<div data-signals="{_loaded: Date.now()}">
This page was opened <sb-relative-time data-attr:datetime="$_loaded" data-preserve-attr="datetime">just now</sb-relative-time>.
</div>
Past and future
datetime takes ISO 8601 or Unix time (seconds or milliseconds).
- Launch:
21 Sep 2026 - Next window:
15 Jan 2027 - First light:
5 Mar 2026
<ul>
<li>Launch: <sb-relative-time datetime="2026-09-21T09:30:00Z">21 Sep 2026</sb-relative-time></li>
<li>Next window: <sb-relative-time datetime="2027-01-15T12:00:00Z" format="short">15 Jan 2027</sb-relative-time></li>
<li>First light: <sb-relative-time datetime="1772668800" numeric="always">5 Mar 2026</sb-relative-time></li>
</ul>
Languages
The page's lang (or the lang attribute) picks the words.
<ul>
<li><sb-relative-time lang="de" datetime="2026-09-21T09:30:00Z"></sb-relative-time></li>
<li><sb-relative-time lang="fr" datetime="2026-09-21T09:30:00Z"></sb-relative-time></li>
<li><sb-relative-time lang="ja" datetime="2026-09-21T09:30:00Z"></sb-relative-time></li>
</ul>
A date after a while
With threshold="7", anything more than a week away shows as a date.
<sb-relative-time threshold="7" datetime="2026-01-01T00:00:00Z">1 Jan 2026</sb-relative-time>
From the server
The datetime attribute belongs to the server. When a Datastar stream re-renders it with a new moment, the text follows:
<p>Last paint <sb-relative-time datetime="2026-09-22T08:14:03Z">08:14</sb-relative-time></p>
Accessibility
It renders a <time datetime="…"> with the full date as its title, so the precise moment is available to assistive technology and on hover.
Installation
Add Datastar with Rocket and the Starbase autoloader once per page, then use the tag. The autoloader imports each component the first time its tag appears, including tags added later by a Datastar morph.
<!-- Once per page: Datastar with Rocket, and the Starbase autoloader.
It loads every <sb-…> component the first time its tag appears. -->
<script type="importmap">
{ "imports": { "datastar": "https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.4/bundles/datastar-rocket.js" } }
</script>
<script type="module" src="https://starbase.zweiundeins.gmbh/c/autoloader.js"></script>
<!-- Optional, no flash of undefined elements: class="sb-cloak" on <html>, and -->
<style>.sb-cloak :not(:defined) { visibility: hidden }</style>
<sb-relative-time datetime="2026-09-22T08:00:00Z" numeric="always">22 Sep 2026</sb-relative-time>
<!-- In production, pin today's catalog instead of the latest: the browser then
refuses any file that changed. Add "integrity" to the import map above: the
hashes from https://starbase.zweiundeins.gmbh/c/@0a1ad1bf98a4/importmap.json and Datastar's, below. -->
<!--
<script type="importmap">
{ "imports": { "datastar": "https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.4/bundles/datastar-rocket.js" },
"integrity": { "https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.4/bundles/datastar-rocket.js": "sha384-vUxZojLrF1Ar3de5h7VINqhJXBgjyZS4U49pHvGa87kum6j5Xn6JRziuARNw5ELG", "…": "…from importmap.json" } }
</script>
<script type="module" src="https://starbase.zweiundeins.gmbh/c/@0a1ad1bf98a4/autoloader.js" integrity="sha384-IvSCUjEWPS+wwXunU+48x96KmyWDx/Y4LeCwCL8cWFqJ/zHmTsTOsSMrzTOcpW3D"></script>
-->
<!-- Or load just this component, pinned to this version: -->
<!-- <script type="module" src="https://starbase.zweiundeins.gmbh/c/relative-time@1e5606a7512d/relative-time.js" integrity="sha384-v7g88TrzuNuhDTMRrBfBg6fcxu9fukTNi9o+LEQSzeEMhhy9a6l2CMuzq5PuBBNI"></script> -->Size
Each file compressed on its own, the way it is served (gzip -9, brotli -11). Datastar and Rocket are shared by every component and not counted.
| File | Original | gzip | brotli |
|---|---|---|---|
relative-time.js | 4.4 kB | 2.0 kB | 1.8 kB |
API reference
Props
| Attribute | Type | Default | Description |
|---|---|---|---|
datetime | string | "" | The moment: ISO 8601 (2026-09-22T08:00:00Z) or Unix time (seconds or ms). |
numeric | "auto" | "always" | "auto" | "auto" allows words like "yesterday"; "always" says "1 day ago". |
format | "long" | "short" | "narrow" | "long" | Length of the units ("3 minutes" / "3 min." / "3m"). |
lang | string | "" | Locale (default: the page's lang, then the browser's). |
threshold | number | 0 | After this many days, show the date instead (0: always relative). |
sync | boolean | true | Keep the text current while the page is open. |