Relative time
Shows when something happened as a distance from now, with the exact time on hover and focus.
Examples
- Ana Marsh refunded order 4812
- support-agent ran eval refund-policy
- Maya Chen published prompt v14
- Jon Reyes revoked key sk_demo_0000aaaa
- Sam Okoye created agent billing-agent
Prompt v14 was last edited by Maya Chen , and the next scheduled eval starts .
- Eval refund-policy started
- Key sk_demo_0000aaaa expires
- Next retry
Format
shortdefault
long
Range
now
minutes
hours
days
past a week
future
States
rest
no hint
Usage
When to use it
- Use
- A row, card or message says when a run, edit or key event happened.
- Use
- A future moment counts down: a retry, an expiry, a scheduled eval.
- Not for
- The exact time is the content, like a log line or an audit export. Use Log viewer.
- Not for
- The date is a fixed fact the person plans around, like a release day. Write the date.
Rules
7 rules
- Must
- Render a moment with RelativeTime; never format "3m ago" by hand. Because a hand-made label goes stale on the page, has no exact time on hover and reads differently from the rest of the product.
- Must
- Pass date as a Date, an ISO string or epoch milliseconds; never a preformatted label. Because the component needs the moment to tick, to build the exact-time hint and to set dateTime on the time element.
- Should
- Use the default short format in rows and tables, and format="long" only inside a sentence. Because "3m ago" stays narrow and lines up on tabular figures, while "3 minutes ago" widens every row of the column.
- Should
- Pass now to freeze the clock in tests, docs and screenshots. Because a live clock changes the label between renders, so a snapshot never matches twice.
- Should
- Keep hint on unless the time sits inside a control that is already a tab stop or already shows the exact time beside it. Because the hint is the only place the exact time appears; hint={false} also removes the extra tab stop.
- Never
- Pass type or color classes to make a time louder; set the color on the parent. Because a time is metadata, and a heavier time in a row competes with the title and the status.
- Should
- Leave past a week to the component, which switches to a calendar date. Because "41d ago" is harder to place than "Sep 2", and a date answers "was that before the release".
Accessibility
4 checks
- The visible label is aria-hidden; a screen reader reads the long form and the exact local time ("14 minutes ago, Oct 13, 2026, 11:46:00 AM").
- The time element carries dateTime in ISO form.
- With a hint it is a tab stop and shows a 2px focus outline; the hint opens on focus, and a tap toggles it on touch.
- It ticks only when the label would change, and not while the tab is hidden.
Edge cases
9 edge cases
- Under 45 seconds
- Reads "now" in both directions.
- Unit boundary
- Turns to the next unit before the rounded value reaches it, so it never reads "60m ago".
- Past a week
- A calendar date, with the year only when it is not this year.
- Invalid date
- Renders "Unknown time" in text-3 with data-invalid.
- Server render
- Reads in UTC as a calendar date with no clock; the browser clock and zone take over after mount.
- A page of 200 times
- Each schedules its own next change, not a shared interval, so none wakes to render the same words.
- Background tab
- Stops ticking and catches up the moment the tab shows again.
- Right to left
- The label is English text; the box stays inline and does not wrap.
- Zoom 200%
- Stays on one line and grows with the text.
API
5 props
dateDate | string | numberdefaultrequiredformat'short' | 'long'default'short'nowDate | string | numberhintbooleandefaulttrueclassNamestring
Tokens
8 tokens
--text-3- oklch(var(--ink-c) / 0.66)
--text-2- oklch(var(--ink-c) / 0.80)
--radius-soft- 6px
--line-focus- 2px
--border-focus- var(--ink)
--focus-offset- 2px
--surface-inverse- var(--ink)
--text-inverse- var(--paper)