Countdown

Time left until a fixed moment — a sale ending, a launch, a booking hold — read from the wall clock on every tick.

Open Source

Installation

Countdown ships with the library — install it once, then import:

import { Countdown } from "@delacour/react-native-ui/countdown";

Or copy the source into your project, to own and edit it:

bunx delacour@latest add countdown

Usage

app/index.tsx
import { Countdown } from "@/components/ui/countdown";
import { useState } from "react";
import { View } from "react-native";

const TWO_DAYS = 2 * 24 * 60 * 60 * 1000;

export default function Example() {
  const [endsAt] = useState(() => Date.now() + TWO_DAYS);

  return (
    <View className="flex-1 items-center justify-center p-4">
      <Countdown to={endsAt} />
    </View>
  );
}

Compute to once — in state, or from your data — never inline in render, or every render moves the target.

Clock accuracy

It reads the wall clock on every tick rather than counting a number down, and re-reads the moment the app returns to the foreground — so it is right after the app was backgrounded, the JavaScript thread stalled or the device slept. Each tick is aimed at the next whole-second boundary, so it never drifts.

Variants

segmented draws each unit in its own box with a caption beneath and slides a changed digit in from above. inline is one run of text for a row or a banner.

Segmented
Inline
<Countdown to={endsAt} />
<Countdown to={endsAt} variant="inline" size="sm" />

Inline reads 2d 14:03:22, 14:03:22 or 3:22 — days lead with a suffix, and every unit after the first is padded to two digits.

Sizes

size steps the digits up the type scale and the box padding with them.

Sizes
Sizes
<Countdown size="sm" to={endsAt} />
<Countdown size="md" to={endsAt} />
<Countdown size="lg" to={endsAt} />

Units

Pick which units to show. The largest absorbs everything above it, so hours and minutes read 48:00 for two days rather than dropping the days.

Units
Units
<Countdown to={endsAt} units={["hours", "minutes", "seconds"]} />
<Countdown to={endsAt} units={["hours", "minutes"]} />

The smallest selected unit rounds up, so a countdown never reads zero before time is actually up.

Trim

With trim, on by default, leading units that read zero are dropped — never below the smallest two selected, so the last minute still reads 0:59. Turn it off to keep every unit in place.

<Countdown to={endsAt} trim={false} />

Urgency

urgentBelow takes seconds; at or below it the digits turn destructive.

Urgent
<Countdown to={holdEndsAt} urgentBelow={60} />

Completion

Seconds round up: the final second reads 0:01, and 0:00 appears only when time is up — which is when onComplete fires, once per to. Give it a new to and it counts again.

<Countdown onComplete={() => setIsExpired(true)} to={endsAt} />

onTick is called with the seconds left whenever they change.

Labels

labels replaces the captions under the boxes, and names the units a screen reader hears.

Labels
Labels
<Countdown labels={{ days: "Tage", hours: "Std.", minutes: "Min.", seconds: "Sek." }} to={endsAt} />

Accessibility

The countdown is one element with the timer role, and the digits are hidden from assistive technology. Its label is coarse — "2 days, 14 hours, 3 minutes left" — and names seconds only in the final minute, so a screen reader is not told every second. Digit motion is skipped on the first render, under the system's reduce-motion setting, and with isMotionCalm on the provider; the count keeps ticking either way.

API Reference

Countdown

Extends ViewProps.

Prop

Type

On this page