Time Display
A timezone-aware clock for your site, with a stable layout as the time updates.
A small timezone-aware clock built for navigation bars, profile pages, and anywhere you want to show a local time.
It starts with an invisible placeholder so server rendering and client hydration always agree. Once mounted, it updates every second. Tabular numbers keep each digit the same width, so the surrounding layout stays still as the seconds change.
Pass any IANA timezone string. Invalid values render as --:--:-- instead of throwing.
Install dependencies
npm install lucide-reactSource code
TimeDisplay.tsx"use client";
import { cn } from "@/lib/utils";
import { Clock } from "lucide-react";
import { useEffect, useState } from "react";
export interface TimeDisplayProps {
timeZone?: string;
showIcon?: boolean;
className?: string;
}
function formatTime(timeZone: string) {
const parts = new Intl.DateTimeFormat("en-GB", {
timeZone,
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hourCycle: "h23",
}).formatToParts(new Date());
const get = (type: Intl.DateTimeFormatPartTypes) =>
parts.find((p) => p.type === type)?.value ?? "00";
return `${get("hour")}:${get("minute")}:${get("second")}`;
}
function isValidTimeZone(timeZone: string) {
try {
Intl.DateTimeFormat("en-US", { timeZone });
return true;
} catch {
return false;
}
}
export default function TimeDisplay({
timeZone = "Asia/Dhaka",
showIcon = true,
className,
}: TimeDisplayProps) {
const [time, setTime] = useState("");
const [invalid, setInvalid] = useState(false);
useEffect(() => {
if (!timeZone.trim() || !isValidTimeZone(timeZone)) {
setInvalid(true);
setTime("--:--:--");
return;
}
setInvalid(false);
const tick = () => {
try {
setTime(formatTime(timeZone));
setInvalid(false);
} catch {
setInvalid(true);
setTime("--:--:--");
}
};
tick();
const id = window.setInterval(tick, 1000);
return () => window.clearInterval(id);
}, [timeZone]);
// SSR / first paint — invisible stable skeleton
if (!time) {
return (
<span
className={cn(
"inline-flex items-center gap-[0.35em] opacity-0",
className
)}
aria-hidden
>
{showIcon ? (
<Clock className="size-[0.85em] shrink-0" strokeWidth={2.25} />
) : null}
<span className="tabular-nums">00:00:00</span>
</span>
);
}
return (
<span
className={cn(
"inline-flex items-center gap-[0.35em] leading-none",
invalid && "text-muted-foreground",
className
)}
>
{showIcon ? (
<Clock
className="size-[0.85em] shrink-0 opacity-70"
strokeWidth={2.25}
aria-hidden
/>
) : null}
<time
className="tabular-nums tracking-tight"
aria-label={
invalid ? "Invalid timezone" : `Current time in ${timeZone}`
}
>
{time}
</time>
</span>
);
}