Skip to content

React Tutorial Practical Projects

React Digital Clock Project with Effects and Intl

Lesson 139 of 156 2 min read By Deepak Paswan Updated Oct 10, 2026

Runnable lesson · press Try it on the examples to edit and run them here.

A clock needs something React cannot do on its own: time passes. That makes it a perfect first project for effects and cleanup.

Requirements

  • Show the current time, updating every second.
  • Let the user switch between 24-hour and 12-hour formats.
  • Let the user choose a time zone from a short list.
  • Clean up the timer when the clock is removed.
  • Do not make a screen reader announce every second.

The solution

App.jsx
JSX
// A reusable hook: re-renders its component every `ms` milliseconds
function useNow(ms = 1000) {
  const [now, setNow] = React.useState(() => new Date());

  React.useEffect(() => {
    const id = setInterval(() => setNow(new Date()), ms);
    return () => clearInterval(id);      // stop the timer when the component goes away
  }, [ms]);

  return now;
}

const ZONES = [
  { value: "UTC", label: "UTC" },
  { value: "Europe/London", label: "London" },
  { value: "Asia/Tokyo", label: "Tokyo" },
  { value: "America/New_York", label: "New York" },
];

function Clock({ timeZone, hour12 }) {
  const now = useNow();
  const time = new Intl.DateTimeFormat("en-US", {
    hour: "2-digit", minute: "2-digit", second: "2-digit", hour12, timeZone,
  }).format(now);
  const date = new Intl.DateTimeFormat("en-GB", {
    weekday: "long", day: "numeric", month: "long", year: "numeric", timeZone,
  }).format(now);

  return (
    <div>
      {/* <time> gives the moment a machine-readable value */}
      <time dateTime={now.toISOString()} style={{ fontSize: 48, fontVariantNumeric: "tabular-nums", fontWeight: 700 }}>
        {time}
      </time>
      <p style={{ margin: 0 }}>{date}</p>
    </div>
  );
}

function App() {
  const [hour12, setHour12] = React.useState(false);
  const [timeZone, setTimeZone] = React.useState("UTC");
  const zoneLabel = ZONES.find((z) => z.value === timeZone).label;

  return (
    <main style={{ fontFamily: "system-ui, sans-serif" }}>
      <h1>Digital clock</h1>
      <p>Showing the time in {zoneLabel}. Format: {hour12 ? "12-hour" : "24-hour"}</p>

      <Clock timeZone={timeZone} hour12={hour12} />

      <p>
        <label>
          Time zone{" "}
          <select aria-label="Time zone" value={timeZone} onChange={(e) => setTimeZone(e.target.value)}>
            {ZONES.map((z) => <option key={z.value} value={z.value}>{z.label}</option>)}
          </select>
        </label>{" "}
        <button onClick={() => setHour12(!hour12)}>{hour12 ? "Use 24-hour" : "Use 12-hour"}</button>
      </p>
    </main>
  );
}

Walkthrough

  1. useNow is a custom hook (Custom Hooks). It owns the timer and returns the current date. Any component can show a live time with one line.
  2. The effect starts the interval and its cleanup stops it (Cleanup Functions). Without clearInterval, removing the clock would leave a timer running.
  3. setNow(new Date()) creates a new value each tick, so React re-renders. The time is state; the formatted string is derived from it.
  4. Intl.DateTimeFormat does the formatting and the time zone conversion. No manual string building and no extra library (Date Formatting). tabular-nums keeps the digits the same width, so the display does not jitter.
  5. The <time> element with a dateTime attribute is the correct semantic element for a time value.
  6. Accessibility. The ticking time is not in a live region. Announcing every second would make the page unusable with a screen reader. The surrounding text (“Showing the time in Tokyo. Format: 12-hour”) changes only when the user acts.

Decisions worth noticing

  • Why a hook and not the interval inside Clock? The timer logic is reusable and separate from the display.
  • Why does the zone and format live in App? The settings belong to the page; Clock only displays. Lifting state up keeps Clock simple (Lifting State Up).
  • Accuracy. setInterval can drift slightly, and browsers slow timers in background tabs. For a display it is fine, because each tick reads the real time from new Date() instead of counting seconds itself. That is more reliable than seconds + 1.
  • Strict Mode. In development, the effect runs twice, and the cleanup prevents two timers (React Strict Mode).

Make it yours

  1. Add a second clock to compare two zones side by side; the same Clock component, two instances.
  2. Add a stopwatch with start, stop and reset using the pattern from useRef.
  3. Show an analog clock with SVG hands whose rotation comes from the hours, minutes and seconds.
  4. Pause updates when the page is hidden, using the visibilitychange event.