Text

RelativeTime

How long ago (or how soon) something happens, in the reader's language: "3 days ago", "yesterday", "in 5 minutes", "now", kept current while it shows. It is a <time> with the exact instant, and its title gives the full local date and time. The server writes the UTC date (in en-US unless locale is set) and hydration repeats it, so the markup matches; the browser then shows the relative time in the reader's language. A calendar date with no time ("2026-06-01") counts whole days from the reader's own today ("tomorrow", "in 3 days"), and its title and <time> carry that day, never moved into a time zone.

Avery

Bridge is open again.

In Spoonjoy

Spoonjoy, a recipe app, is built with Ouro HX (how).

See it live, no sign-in needed: A recipe

In its source: app/components/recipe/SpoonsStrip.tsx · app/components/users/FellowChefList.tsx

Install

Once per app, with Tailwind CSS v4 and an @/* path alias (Ouro HX is built on Base UI, so --base base; skip -t in an existing app):

npx shadcn@latest init --base base --preset nova --yes --no-monorepo
npx shadcn@latest add ourostack/ouro-ui/ouro-base#v1.2.13

Then the component. From v0.8.0 it installs only the helpers it imports and never touches your stylesheet (Installing):

npx shadcn@latest add ourostack/ouro-ui/relative-time#v1.2.13

Or, with the @ouro namespace set up:

npx shadcn@latest add @ouro/ouro-base @ouro/relative-time

Its tests, stories and Playwright spec are optional. They need Vitest 4.1.11 or later, and from v0.8.0 never change a version your app has (Testing):

npx shadcn@latest add ourostack/ouro-ui/relative-time#v1.2.13 ourostack/ouro-ui/relative-time-tests#v1.2.13
import { RelativeTime } from "@/components/ouro/relative-time/relative-time";

Paths use shadcn's default @/ alias; the CLI rewrites them to yours.

Example

<RelativeTime value={comment.createdAt} />

Use instead when

  • Use LocalDate instead where the date itself matters, such as a due date.

Props

Type: RelativeTimeProps.

className
Type: string
Layout only. Appearance classes warn in development.
locale
Type: Intl.LocalesArgument
Default: the reader's (en-US in the server's text, until the browser takes over)
The language and region to write it in.
numeric
Type: "auto" | "always"
Default: "auto"
auto says "yesterday" and "now"; always says "1 day ago" and "in 0 seconds".
ref
Type: Ref<HTMLTimeElement>
Reaches the rendered element.
value (required)
Type: Date | string | number
The instant: a Date, an ISO string or a timestamp in milliseconds. A calendar date with no time ("2026-06-01", or "2026-06" for a month) counts whole days (or months) in the reader's own calendar and is never moved into a time zone.

Native attributes

Also accepts the attributes in React's HTMLAttributes and TimeHTMLAttributes (id, aria-*, event handlers), passed to the rendered element, except those named on this page.

Don't use

These are not props. Passing one fails the type check with the fix in the error, and in development a console message names the fix.

  • date: Not a prop. The instant is value: <RelativeTime value={comment.createdAt} />.
  • now: Not a prop. The time updates itself; nothing to pass.

className is for layout only

Pass margins, width and grid placement (mt-4, w-full, col-span-2). Your classes always win over Ouro UI's: RelativeTime merges className last with cn (tailwind-merge), so a class that conflicts with its own replaces it.layout-classes

Appearance comes from props, not classes. In development, a class that sets appearance logs one warning per component instance, naming the prop to use instead. A class sets appearance when, after its variants (hover:, sm:) and any ! are removed, it starts with bg-, text-, border-, rounded-, shadow-, font-, ring-, from-, via-, to-, fill- or stroke-, or is border, rounded, shadow or ring, or has a dark: variant. Text wrapping, overflow and table layout classes (text-wrap, text-ellipsis, border-collapse) are layout and do not warn.

Alignment classes (text-center) warn too: set alignment on a parent element.

Claim sources