shadcn.io is not affiliated with official shadcn/ui
Each press adds incrementBy to the number beside the label, and every digit that changes rolls to its new value like an odometer, while the others stay still.
Use it for likes, upvotes, add-to-cart and guest counts: small numbers someone nudges with a press. For a number people type or set precisely, use a number field instead. It wraps the Button, so variant and size work as usual. Each digit is a column of 0 to 9 behind a one-digit window, slid by a CSS transition, with no animation library; with reduced motion the digits change in place without rolling.
Installation
Usage
import { CounterButton } from "@/components/ui/counter-button";<CounterButton defaultCount={128}>Like</CounterButton>Demos
Step
incrementBy is what one press adds, and the default is 1. A box of 5 adds 5 at a time; a negative step counts down, as a downvote does. Here min stops the downvotes at 0.
Controlled
Keep the number in your own state: pass it as count and update it from onCountChange. Here a Clear button sets it back to 0 and the digits roll down to it, and max stops the guest list at 6.
Accessibility
The rolling digits are hidden from assistive tech. The button carries the plain number instead, in a polite status inside it, so it's announced as its label followed by the count, such as Like 128, and each new count is read out after a press. It presses with Enter and Space like any button.
- Enter
- Add one step to the count.
- Space
- Add one step to the count.
FAQ
How do I start from an existing count?
Pass defaultCount, such as the number of likes you loaded. It's 0 by default, and the button keeps counting from there on its own. To hold the number yourself, pass count and onCountChange instead, as in the Controlled demo; the digits roll whenever count changes.
How do I save the new number?
Use onCountChange: it gets the new number after each press that changes it.
onClick still gets the click event as usual.
Can it count down?
Yes. Give incrementBy a negative number, such as -1 for a downvote, and min={0} to stop at zero. Each digit rolls to its new value whichever way the count goes.
How do I stop it at a maximum?
Pass max, and min for a floor. A press that would go past either stops at it, and a press at the limit does nothing. To show that, disable the button once your stored count reaches the limit.
Pass disabled.
How do I put a heart next to the label?
Pass it as icon: it goes before the label, and the number after. The preview at the top of this page uses a heart.
API Reference
CounterButton
data-slot="counter-button-count"
count?number
The number it shows, when you hold it yourself. Pair it with
onCountChange.
defaultCount?number
The number it starts from when you don't pass count.
- Default
0
onCountChange?(count: number) → void
Called with the new number after each press that changes it.
- Parameters
count– number- Returns
void
children?ReactNode
The label before the count.
focusableWhenDisabled?boolean
Whether the button should be focusable when disabled.
- Default
false
icon?ReactNode
An icon before the label, such as a heart or a cart.
incrementBy?number
What one press adds. A negative step counts down.
- Default
1
max?number
The highest number a press can reach; a press past it stops there.
min?number
The lowest number a press can reach; a press past it stops there.
nativeButton?boolean
Whether the component renders a native <button> element when replacing it
via the render prop.
Set to false if the rendered element is not a button (for example, <div>).
- Default
true
render?ReactElement | ((props: object, state: object) => ReactElement)
Replace the host element or compose it with another component.
- Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string
Merged with the part's classes.
--counter-button-digitstring
The digit a column shows, 0 to 9 (set per digit).
Related
- Pro
GitHub Stars Button
Hover revealsA live count instead of a local one
Icon Button
Hover revealsIcon toggle for likes without a number
Copy Button
Progress & resultsPress feedback that swaps its content
- Pro
Success Button
Progress & resultsConfirms the press with a checkmark
- Pro
Input Button
ControlsAnother control that takes a value
Ripple Button
Motion & feedbackTactile feedback on every press