use-counter
Create a clamped counter whose every transition stays inside [min, max].
| Package | @vizejs/composable/use-counter |
| Own the source | vize lib pull composable:use-counter |
| Runtime exports | useCounter |
| Gzip budget | 1536 B |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/composable
Import the entry directly. Call lifecycle-bound utilities inside <script setup> so cleanup follows the component scope; consult the runtime contract for this entry's SSR and hydration behavior.
import { useCounter } from "@vizejs/composable/use-counter";
Try it
Reserve workshop seats
Keep a seat quantity between one and five and explain why further changes are unavailable.
What to observe: Add seats until the limit message appears. Add and remove controls disable at their respective bounds; Reset restores the initial one-seat reservation.
Mutate the readonly count through its controls. atMin and atMax are computed refs. Bounds are fixed when the composable is created.
Compare the initial and interacted states


Copy the example
Save this complete SFC in your Vue project. Its public imports and source are the same ones executed in the preview.
<script setup lang="ts">
import { useCounter } from "@vizejs/composable/use-counter";
const { count, atMin, atMax, increment, decrement, reset } = useCounter(1, { min: 1, max: 5 });
</script>
<template>
<div class="composable-example">
<p>Reserve up to five workshop seats.</p>
<div class="example-actions">
<button type="button" :disabled="atMin" @click="() => decrement()">Remove seat</button>
<output aria-live="polite">{{ count }} {{ count === 1 ? "seat" : "seats" }}</output>
<button type="button" :disabled="atMax" @click="() => increment()">Add seat</button>
<button type="button" @click="() => reset()">Reset seats</button>
</div>
<p v-if="atMax">You reached the five-seat limit.</p>
</div>
</template>
Runtime contract
| Utility | Category | Stability | SSR | Hydration | Cleanup | Targets | Host globals | Uses |
|---|---|---|---|---|---|---|---|---|
useCounter |
state | experimental | safe | stable | none | web, server, worker, native, desktop, terminal | — | — |
API
useCounter
Create a clamped counter whose every transition stays inside [min, max]. All operations clamp instead of failing, including the initial value, so the count is inside the bounds at every observable moment. Only NaN is rejected — silently corrupting the count is never an option. Purely synchronous state: safe during server rendering (no browser globals, no timers) and nothing to dispose, so it works inside and outside reactive scopes alike. Bounds are fixed at creation and not reactive.
function useCounter(initial = 0, options: UseCounterOptions = {}): CounterControls
const { count, increment, atMax } = useCounter(9, { min: 0, max: 10 });
increment(); // 10
increment(); // 10 (clamped)
atMax.value; // true
Types
UseCounterOptions
Options for useCounter.
| Member | Type | Description |
|---|---|---|
min? |
number |
Inclusive lower bound applied to every value the counter takes. |
max? |
number |
Inclusive upper bound applied to every value the counter takes. |
CounterControls
Reactive controls returned by useCounter.
| Member | Type | Description |
|---|---|---|
count |
Readonly<ShallowRef<number>> |
Current count. Changes only through the controls, never by assignment. |
atMin |
ComputedRef<boolean> |
Whether the count currently sits on the configured lower bound. |
atMax |
ComputedRef<boolean> |
Whether the count currently sits on the configured upper bound. |
increment |
(delta?: number) => number |
Add delta (default 1) to the count and clamp into the bounds. |
decrement |
(delta?: number) => number |
Subtract delta (default 1) from the count and clamp into the bounds. |
set |
(value: number) => number |
Assign a value directly, clamped into the bounds. |
reset |
(value?: number) => number |
Restore the reset baseline, or establish a new one. Without an argument the count returns to the creation-time initial value (after its original clamping). With an argument, the clamped value becomes both the new count and the baseline used by later reset() calls. |