shadcn.io is an independent project built on shadcn/ui
useCountdown keeps a number and three controls: startCountdown, stopCountdown, and resetCountdown.
Use it for a sale clock, a session timeout, or a quiz timer. For a count you change only with buttons, use useCounter. In React the interval stays off until startCountdown runs, so the first paint is just countStart.
Installation
Anatomy

Usage
import { useCountdown } from "@/hooks/use-countdown";const [count, { startCountdown, stopCountdown, resetCountdown }] =
useCountdown({ countStart: 30 });countStop defaults to 0, intervalMs to 1000, and isIncrement to false, so it counts down once a second until it hits the stop value.
FAQ
Can the countdown count up?
Yes. Pass isIncrement: true and the tick adds one instead of subtracting one. It still stops only when count equals countStop. If you count up from a start that is already above countStop, the numbers never match and the timer keeps going. Set countStop to the value where it should halt.
Does the countdown stop at zero by itself?
It stops when the count equals countStop, which defaults to 0. The comparison runs at the start of a tick, so the number lands on the stop value and sits there. One interval later the timer clears itself without changing the number again. Pass a different countStop when the end should not be zero.
How do I change the countdown speed?
Pass intervalMs. The default is 1000, so one tick per second. The interval is created only while the countdown is running, and it is rebuilt when intervalMs changes. A smaller number ticks faster. The step is still one. This option does not change how much is added or subtracted.
Can I pause and resume the countdown?
Yes. stopCountdown sets the running flag to false and leaves the current number in place. startCountdown sets that flag back to true, and the next ticks continue from the number you already have. resetCountdown stops and sets the number back to countStart. None of the three change the options.
What if I start when the count already equals the stop value?
The next tick sees that they are equal, turns the timer off, and does not add or subtract. startCountdown itself only sets a flag. It does not look at the numbers. So a countdown that begins on countStop waits one intervalMs and then stops, still showing that same number.
Is the countdown safe on the server?
Yes. The hook only uses state and effects. No interval is created until startCountdown runs in the browser. The first render is countStart on the server and the client. Changing countStart later does not move the current number. It only changes what resetCountdown writes back.
API Reference
Parameters
optionsCountdownOptions
Where it starts and stops, how fast, and which way.
options.countStartnumber
The count it starts from, and goes back to on reset.
options.countStop?number
The count it stops at.
- Default
0
options.intervalMs?number
Milliseconds between steps.
- Default
1000
options.isIncrement?boolean
Counts up instead of down.
- Default
false
Returns
[0] countnumber
The current count.
[1] controllersCountdownControllers
Functions that start, stop and reset it. Stable across renders.
controllers.resetCountdown() => void
Stops the countdown and puts the count back to countStart.
- Type
() => void
controllers.startCountdown() => void
Starts (or resumes) counting from the current count.
- Type
() => void
controllers.stopCountdown() => void
Pauses the countdown, keeping the current count.
- Type
() => void











