Azalea

RelativeTime

When something happened, in the one notation the whole suite agrees on.

Why it exists

Eight copies of this function existed across the suite — ago() in Echo, Ivy, Root, Sage and Bloom, relative() in Seed and Grove, one more inlined in Branch's issue table. They did not merely duplicate each other; they disagreed, and someone moving between two Trellis apps during an incident had to re-read the same fact in a different notation.

The factBeforeNow
40 seconds agoEcho 40s ago · Sage just now
90 seconds agoRoot 1m ago · Sage 2m ago
26 hours agoBloom 26h ago · Seed 1d ago
418 days agoRoot 418d ago · Seed 2025-07-17
no timestampRoot never · Echo —
four hours from nowall three: just now

The scale

One unit, floored, and an absolute date past a fortnight. Hover any of these — the exact UTC instant is in the title, so a column stays scannable without the precision going missing.

Forward, for a deadline

future flips the scale round. Anything already due says so rather than counting down past zero — 'in 0m' reads as a countdown that has stalled.

The absolute form

For an incident timeline, an audit row, anywhere two timestamps are compared to each other. Monospace and tabular, because the digits have to line up or the comparison is eyeballing rather than reading.

In both themes

light

dark

Guidance

Use it when

  • A column of records where the age is what you are scanning for.
  • future for anything with a deadline — a rotation, an expiry, a scheduled send.
  • Timestamp when two instants will be compared to each other.

Reach for something else when

  • A duration. That is duration(ms), which shows two units because a span is the measurement rather than a label on one.
  • Anywhere the exact instant is the point and hovering is not possible — print it.
  • Live ticking. This renders once; a value that changes every second is a hydration mismatch waiting to happen.

Props

PropTypeDefaultDescription
valueDate | string | number | nullThe instant. A Date, an ISO string, or epoch milliseconds.
futurebooleanfalseCount forward — “in 4h” — for a deadline rather than a record.
nowDate | numberPins the clock. For tests, and for a page rendering many rows off one instant.
fallbackstring"never"Shown when there is no timestamp. “—” when absence is not a fact.

Accessibility, and hydration

It renders a real <time dateTime>, so the machine-readable instant travels with the human one — it survives being copied, scraped, or read by a screen reader that announces dates in the user's own format.

It also carries suppressHydrationWarning. This renders on the server and hydrates milliseconds later, and the two can land either side of a minute boundary. Both values are correct; only React minds. The suppression covers this element's text and nothing else — which is also why the scale stops at whole minutes rather than counting seconds.