---
title: Clock
description: The reader's own day and time, in the reader's own zone, without asking anybody for a location.
source: https://adamjurek.com/components/clock
---

## Home


Clock renders the reader's own local weekday and time, using `Intl.DateTimeFormat`
with no locale or time zone set. Reach for it in a footer, a status bar, or
anywhere a page wants a live clock without asking for a location or talking to
a server.

<!-- ::start:showcase demo="clock" height="380" -->
<!-- ::end:showcase -->

## Why it is built this way

It renders nothing on the server, on purpose. A server has its own clock and
zone, so a real time rendered there would say one thing while the first client
render says another - a hydration mismatch, which React answers by discarding
and rebuilding the whole tree. Clock shows a placeholder on both the server
and the first client frame, then fills in the real value from an effect
afterward, so there is never a mismatch to suppress.


## Install

<!-- ::start:tabs -->

### TanStack

```shell
tanstack add https://adamjurek.com/r/tanstack/clock.json
```

### shadcn

```shell
pnpm dlx shadcn@latest add https://adamjurek.com/r/shadcn/clock.json
```

### pnpm

```shell
pnpm add @sushindustries/ui @sushindustries/atoms
```

<!-- ::end:tabs -->

## What you get

| | |
| --- | --- |
| Version | 0.1.0 |
| Category | content · Rendering |
| Files | `clock.tsx` |
| Dependencies | None |
| Tags | intl, ssr, no-deps |

> [!NOTE] No runtime dependencies
> It brings nothing with it beyond the stylesheet.

## Get Started


Install commands are on Home, attached from the registry - they are not written
here, because a second copy is a copy that goes stale. This tab starts after the
install worked.

## Use it

```tsx
import { Clock } from "@sushindustries/ui";

export function Example() {
	return <Clock />;
}
```

## What you should see

`--:--` for the first render, then your own weekday and time, in your own
zone - short weekday, hour, minute. It updates every fifteen seconds, not
every second: a clock showing minutes has no reason to re-render sixty times
a minute.

## If nothing happens

The placeholder on first paint is correct, not a bug - it is what both the
server and the first client frame render, on purpose. If the real time never
arrives, the component itself never mounted in the browser: check it is not
stuck inside something that never hydrates. The time value itself cannot be
wrong, since it comes straight from `Intl.DateTimeFormat` with no locale or
zone passed - whatever the browser already knows.


## Guides


## Choosing the format

`options` is passed straight to `Intl.DateTimeFormat`, so anything that
constructor accepts works here, not just the weekday-and-time default.

```tsx
<Clock
	options={{
		weekday: "long",
		hour: "2-digit",
		minute: "2-digit",
		second: "2-digit",
	}}
	every={1000}
/>
```

Pair a finer `options` with a finer `every`: a clock with seconds in it
should not sit for up to fifteen seconds before it agrees with the wall
clock, and a clock without seconds has no reason to poll every one.

## Why it never renders on the server

The server has its own clock and zone. Rendering a real time there means the
markup says one thing and the first client render says another - a hydration
mismatch, and React discards the whole tree to fix it. `Clock` renders the
placeholder on both sides and fills in the real value from an effect
afterward, which is also why there is no `suppressHydrationWarning` here:
there is nothing to suppress.

## When not to use it

A clock that has to agree with a server-known value - a countdown to a
deadline, a "posted 3 minutes ago" - is not this component. `Clock` only ever
reads the reader's own device; wire that case up separately.


## API


<!-- generated:api -->

## Props

| Prop | Type | Default | Does |
| --- | --- | --- | --- |
| `every?` | `number` | `15_000` | How often to re-read the time, in milliseconds. A clock showing minutes has no reason to tick every second: fifteen seconds is close enough that the displayed minute is never wrong for long, and it is four wake-ups a minute instead of sixty. |
| `options?` | `Intl.DateTimeFormatOptions` | `DEFAULT` | Passed straight to `Intl.DateTimeFormat`. |
| `placeholder?` | `string` | `"--:--"` | Shown until the first client render. |

<!-- /generated:api -->

## Notes

`options` is passed straight to `Intl.DateTimeFormat` with no validation - an
invalid combination throws inside the effect, exactly as it would calling the
constructor by hand. `every` and `options` are independent: nothing keeps a
fast interval in step with a coarse format or the other way round, so pick
both together (see Guides).

`placeholder` only ever appears once, before the first tick. There is no prop
that brings it back once a real time has been read.


## Examples


Examples are the tab where the component is shown doing a job, not
demonstrating a prop. The API tab already lists the props.

<!-- ::start:showcase demo="clock" height="420" -->
<!-- ::end:showcase -->

Press Compare. The frames are real viewports, so a layout that breaks at 320
breaks here too rather than in somebody's hands.

## In a page

```tsx
import { Clock } from "@sushindustries/ui";

export function SiteFooter() {
	return (
		<footer className="flex items-center justify-between gap-3">
			<span className="fg-faint text-xs">© Sushi Industries</span>
			<Clock />
		</footer>
	);
}
```

## What this example is not

Not proof the clock is indexable. A crawler that does not run JavaScript sees
only the placeholder, same as the first paint does - fine for a footer clock,
wrong for anything meant to be read as content.
