motion-counter
Animates a number from one value to another using spring physics when it enters the viewport. Great for stats, scores, and dashboards.
Preview
Import
import 'motion-components/motion-counter' // just this one
import 'motion-components' // or the whole libraryUsage
<motion-counter from="0" to="9847" duration="1.8" suffix="+"></motion-counter>How it works
The counter watches the viewport via IntersectionObserver. When 20% of the element is visible, it animates the number from from to to using a spring. By default (once), it only fires once and disconnects the observer.
Prefix & suffix
Attach static text before or after the number. This is useful for currency symbols, units, and percentages.
<!-- Currency -->
<motion-counter from="0" to="2.4" decimals="1" prefix="$" suffix="M"></motion-counter>
<!-- Percentage -->
<motion-counter from="0" to="98" suffix="%"></motion-counter>
<!-- Score -->
<motion-counter from="0" to="9847" suffix="+"></motion-counter>Decimals
Control how many decimal places are displayed. The spring animates through fractional values so the decimal digits flow naturally.
<motion-counter from="0" to="4.87" decimals="2" suffix=" GB"></motion-counter>Repeat on re-entry
once defaults to true. Set once property to false.
<motion-counter from="0" to="100" once="false"></motion-counter>Loop
Add loop to count to to, hold, snap back to from and count again, like a
dashboard that keeps ticking. hold sets how long the final number stays, gap how long it
sits back at the start.
Requests per second:
<motion-counter from="120" to="480" loop hold="1.2" gap="0.4"></motion-counter>
With loop the cycle runs only while the counter is on screen, and once is ignored. Add
pause-on-hover to freeze the cycle while the pointer is over it.
Methods
| Method | Description |
|---|---|
| | Restart the animation from |
const counter = document.querySelector('motion-counter')
counter.replay()Properties
| Attribute | Property | Type | Default | Description |
|---|---|---|---|---|
| | | number | 0 | Starting value |
| | | number | 100 | End value |
| | | number | 1.5 | Spring duration hint in seconds |
| | | number | 0 | Number of decimal places to display |
| | | string | '' | Static text prepended to the number |
| | | string | '' | Static text appended to the number |
| | | boolean | true | Animate only on first intersection. Set to |
| | | boolean | false | Count to |
| | | number | 1.6 | With |
| | | number | 0.5 | With |
| | | boolean | false | Pause the loop while the pointer is over the counter, resuming on leave |
JavaScript API
Trigger the counter programmatically with play() and cancel().
Accessibility
When prefers-reduced-motion is set, the counter jumps instantly to its final value with no animation,
and loop never starts.