Display
Formatted Date
Locale-aware date and time formatter that renders a semantic <time> element, with absolute and relative styles powered by the Intl APIs.
datetimeintlformatterlocalerelative
Formats
Absolute formatting via the fallback format style (German locale).
Short:
Medium:
Long:
Full:
With time
Combine dateStyle and timeStyle for a date plus a time.
Date + short time:
Date + long time:
Relative time
Human-friendly relative output via Intl.RelativeTimeFormat.
5 minutes ago:
2 hours from now:
3 days ago:
Locales
The same date rendered across several locales.
German:
English:
French:
Spanish:
Italian:
Convenience components
ShortDate, DateTime and RelativeTime wrap the common patterns.
ShortDate:
DateTime:
RelativeTime:
Source
Copy this into your project. Resolve its dependencies from the registryDependencies in the component's API entry.
/**
* FormattedDate — locale-aware date/time formatting
*
* A presentational formatter that renders a semantic <time> element using the
* platform Intl APIs. Supports absolute formatting (dateStyle / timeStyle) and
* relative formatting ("5 minutes ago") via Intl.RelativeTimeFormat. No styling
* of its own — pass `className` to style the rendered <time>.
*/
export interface FormattedDateProps {
/** The date to format. Accepts a Date, an ISO string, or a timestamp. */
date: Date | string | number
/** Fallback style used when neither dateStyle nor timeStyle is set. */
format?: "short" | "medium" | "long" | "full"
/** Time portion style (Intl.DateTimeFormat timeStyle). */
timeStyle?: "short" | "medium" | "long" | "full"
/** Date portion style (Intl.DateTimeFormat dateStyle). */
dateStyle?: "short" | "medium" | "long" | "full"
/** Render as relative time (e.g. "in 2 days", "5 minutes ago"). */
relative?: boolean
/** Class applied to the rendered <time> element. */
className?: string
/** BCP 47 locale tag. Defaults to "de". */
locale?: string
/** IANA time zone. Defaults to "Europe/Berlin". */
timeZone?: string
}
export function FormattedDate({
date,
format = "medium",
timeStyle,
dateStyle,
relative = false,
className,
locale = "de",
timeZone = "Europe/Berlin",
}: FormattedDateProps) {
const dateObj = new Date(date)
// Relative time formatting
if (relative) {
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: "auto" })
const now = new Date()
const diffInMs = dateObj.getTime() - now.getTime()
const diffInMinutes = Math.round(diffInMs / (1000 * 60))
const diffInHours = Math.round(diffInMs / (1000 * 60 * 60))
const diffInDays = Math.round(diffInMs / (1000 * 60 * 60 * 24))
let value: number
let unit: Intl.RelativeTimeFormatUnit
if (Math.abs(diffInDays) >= 1) {
value = diffInDays
unit = "day"
} else if (Math.abs(diffInHours) >= 1) {
value = diffInHours
unit = "hour"
} else {
value = diffInMinutes
unit = "minute"
}
return (
<time dateTime={dateObj.toISOString()} className={className}>
{rtf.format(value, unit)}
</time>
)
}
// Absolute formatting
const options: Intl.DateTimeFormatOptions = { timeZone }
if (dateStyle || timeStyle) {
if (dateStyle) options.dateStyle = dateStyle
if (timeStyle) options.timeStyle = timeStyle
} else {
options.dateStyle = format
}
try {
return (
<time dateTime={dateObj.toISOString()} className={className}>
{new Intl.DateTimeFormat(locale, options).format(dateObj)}
</time>
)
} catch {
// Fallback if the locale is unsupported or the date is invalid
return (
<time dateTime={dateObj.toISOString()} className={className}>
{dateObj.toLocaleDateString("de", { timeZone, dateStyle: format })}
</time>
)
}
}
/** Convenience: compact date, no time. */
export function ShortDate({
date,
className,
locale,
}: {
date: Date | string | number
className?: string
locale?: string
}) {
return <FormattedDate date={date} format="short" className={className} locale={locale} />
}
/** Convenience: medium date with a short time. */
export function DateTime({
date,
className,
locale,
}: {
date: Date | string | number
className?: string
locale?: string
}) {
return (
<FormattedDate
date={date}
dateStyle="medium"
timeStyle="short"
className={className}
locale={locale}
/>
)
}
/** Convenience: relative time (e.g. "2 hours ago"). */
export function RelativeTime({
date,
className,
locale,
}: {
date: Date | string | number
className?: string
locale?: string
}) {
return <FormattedDate date={date} relative className={className} locale={locale} />
}