Skip to content
Sign in
Apps SDKCountdown widgetMenu

Countdown widgetPro

A timer on a minute tape you drag to set, or a countdown to a moment in steady tabular digits.

ProA focus block running down on a minute tape: pause it, add a minute, or drag the tape to set a new length.

Installation

$ pnpm dlx shadcn@latest add @chatcn-pro/countdown-widget

Usage

tsx
import { CountdownWidget } from "@/components/ui/chatcn/pro/countdown-widget"

<CountdownWidget
  title="Focus on the specimen copy"
  defaultDuration={25 * 60}
  onDone={() => notify("Time to stretch")}
/>

<CountdownWidget
  mode="until"
  title="Doors open, Porto type night"
  target="2026-11-14T19:00:00Z"
  remind={remind}
  onRemindChange={setRemind}
/>

As a timer, the signature is the dial: a minute tape that slides under a fixed needle, like a kitchen timer pulled out flat. Drag it, or focus it and use the arrow keys, to set the length in whole minutes up to max. While it runs the tape moves every frame and the line under the digits says when it ends in your local time. At zero the digits and needle turn amber, the end is announced, and onDone fires once.

defaultStatus and defaultRemaining restore a timer that was already running or paused, for example when the host reloads the widget. +1 min adds a minute in any state.

With mode="until", it counts to target in days, hours, minutes and seconds that change together on the second. onRemindChange adds the reminder toggle; once the moment arrives it says so and shows onJoin as the action. Pass now to freeze the clock for screenshots and tests. Render it inside the card your host draws for a widget answer. It fills the width it is given, so a comfortable max width (around 28 rem) comes from the card, and it follows the host theme through the kit tokens. Pass onExpand where the host can go full screen.

Examples

States

ProCounting to a date, under an hour with a reminder, paused, time up, the moment arrived, disabled, loading and an error.

Props

PropTypeNotes
title, metaReactNode, ReactNode[]The header
mode"timer" | "until"Default timer
duration, defaultDuration, onDurationChangenumberSeconds. Default 25 minutes
maxnumberLongest length on the dial, in minutes. Default 60
defaultStatus"idle" | "running" | "paused" | "done"Where the timer starts
defaultRemainingnumberSeconds left when restoring
onStart, onPause, onReset, onDonecallbacksTimer events
targetstring | number | DateFor until
remind, onRemindChangebooleanThe reminder toggle, for until
onJoin() => voidThe action once the moment arrives
nownumberA fixed clock in ms
locale, timeZonestringFor the end time
loading, error, disabledboolean
onRetry, onExpand() => void
labelsPartial<…Labels>Every string the widget writes, for translation

Labels

labels covers the buttons, the dial name and minutes(n), endsAt(time), paused, the hint, the done line and doneAlert(title), the unit names, the reminder, the arrived line and join, remaining(text) for the spoken countdown, and the error copy.

Keyboard

KeyAction
Arrow keys on the dialOne minute less or more
Page Up, Page DownFive minutes more or less
Home, EndOne minute, or the longest
Enter or Space on StartStart, pause or resume

Accessibility

  • The dial is a slider with its value in minutes; it is disabled while the timer runs.
  • The digits are a timer role that does not announce every second; the end is announced once, assertively.
  • The countdown reads as one sentence (2 days, 4 hours, 12 min to go) rather than four numbers.
  • Done carries a bell icon and words, not only amber.

Motion

The tape moves every frame while the timer runs and follows a drag 1:1, then settles on the nearest minute in 180 ms. Keyboard changes jump. Digits never animate. Reduced motion moves the tape once a second.