# Odometer: Default

> Step the number up and down. Wheels roll the way the value moves, only the digits that change turn, and 999 → 1,000 widens as the new digit and comma roll in. Click fast: they redirect mid-roll.

A shadcn/ui block from the [@keyframeui](https://keyframeui.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/keyframeui/odometer-demo).

- Page, with a live preview: https://shadcn.exchange/r/keyframeui/odometer-demo
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/keyframeui/odometer-demo-light-f2f3255b1f8bdd8b.png
- Registry item: https://keyframeui.com/r/odometer-demo.json
- Categories: number, data, motion
- Listed: 2026-10-14, source last changed 2026-10-15

## Install

```bash
npx shadcn@latest add https://keyframeui.com/r/odometer-demo.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `https://keyframeui.com/r/odometer.json`

## Notes from the registry

Live preview, examples and props: https://keyframeui.com/components/odometer#odometer-demo

## Files

### `registry/demos/odometer-demo.tsx`

```tsx
"use client";

import * as React from "react";

import { Odometer } from "@/components/ui/odometer";

const MAX = 9_999_999;

const steps = [
  { label: "−100", name: "Subtract 100", apply: (n: number) => n - 100 },
  { label: "−1", name: "Subtract 1", apply: (n: number) => n - 1 },
  { label: "+1", name: "Add 1", apply: (n: number) => n + 1 },
  { label: "+100", name: "Add 100", apply: (n: number) => n + 100 },
  { label: "×10", name: "Multiply by 10", apply: (n: number) => n * 10 },
];

const button =
  "h-9 min-w-12 rounded-xl px-3 font-mono text-sm text-zinc-600 tabular-nums transition-[background-color,color,scale] duration-150 outline-none select-none hover:bg-zinc-100 hover:text-zinc-950 focus-visible:ring-2 focus-visible:ring-zinc-400 active:scale-[0.94] dark:text-zinc-400 dark:hover:bg-zinc-800 dark:hover:text-zinc-50 dark:focus-visible:ring-zinc-600";

// Click fast: every wheel turns from wherever it is.
export function OdometerDemo() {
  const [value, setValue] = React.useState(1284);
  const set = (next: number) => setValue(Math.min(MAX, Math.max(0, Math.round(next))));

  const random = () => {
    // A random length too, so the commas come and go.
    const digits = 1 + Math.floor(Math.random() * 7);
    set(Math.random() * 10 ** digits);
  };

  return (
    <div className="flex w-full max-w-xl flex-col items-center">
      <p className="text-[clamp(3.5rem,14vw,6.5rem)] leading-none font-semibold tracking-[-0.045em] text-zinc-950 dark:text-zinc-50">
        <Odometer value={value} />
      </p>
      <div className="mt-12 flex flex-wrap items-center justify-center gap-0.5 rounded-2xl border border-zinc-200 bg-white p-1 shadow-sm shadow-zinc-950/5 dark:border-zinc-800 dark:bg-zinc-900 dark:shadow-black/40">
        {steps.map((step) => (
          <button
            key={step.label}
            type="button"
            aria-label={step.name}
            onClick={() => set(step.apply(value))}
            className={button}
          >
            {step.label}
          </button>
        ))}
        <span aria-hidden className="mx-1 h-5 w-px bg-zinc-200 dark:bg-zinc-800" />
        <button type="button" onClick={random} className={button}>
          Random
        </button>
      </div>
    </div>
  );
}
```
