Vue
@watchstop/vue adapter.
Composables bridging Store into a shallow ref, cleaned up with onScopeDispose. @watchstop/core is a peer dependency — install both.
Exact public names
useStopwatch is the entire public API.
type UseStopwatchOptions =
| { clock?: Clock; precisionMs?: number }
| { stopwatch: Stopwatch }
type StopwatchBinding = {
elapsed: Readonly<ShallowRef<number>>
running: Readonly<ShallowRef<boolean>>
start: () => void
stop: () => void
reset: () => void
stopwatch: Stopwatch
}
declare function useStopwatch(options?: UseStopwatchOptions): StopwatchBindinguseStopwatch
useStopwatch owns a Stopwatch and its teardown, so a component that needs its own timer imports one thing and holds no instance itself.
<script setup lang="ts">
import { useStopwatch } from '@watchstop/vue'
const { elapsed, running, start, stop, reset } = useStopwatch()
</script>
<template>
<p>{{ elapsed }} ms</p>
<button @click="running ? stop() : start()">{{ running ? 'Stop' : 'Start' }}</button>
<button @click="reset">Reset</button>
</template>- Construction is inert. Nothing is scheduled until
start(). setupruns once, sostart,stop, andresetare bound once and keep the same identity for the life of the component.onScopeDisposecallsdestroy()when the owning effect scope (component oreffectScope) stops, after the unsubscribe registered for the ref.elapsedis a read-only shallow ref.stopwatchis the owned instance, exposed for passing elsewhere; do not calldestroy()on it yourself.
Options
| Option | Type | Purpose |
|---|---|---|
clock | Clock | Owned mode: use this clock instead of detectClock(). Pass createMockClock() in tests. |
precisionMs | number | Owned mode: coarsen notify cadence — see Options. |
stopwatch | Stopwatch | Borrowed mode: bind this instance; do not pass clock / precisionMs. |
Sharing one stopwatch across components
Pass the same core instance into each composable:
<script setup lang="ts">
import { Stopwatch } from '@watchstop/core'
import { useStopwatch } from '@watchstop/vue'
const session = new Stopwatch()
const { elapsed, running, start, stop, reset } = useStopwatch({
stopwatch: session,
})
</script>The adapter never calls destroy() on a borrowed instance. Own teardown yourself when the session ends, or leave a module-level instance alive for the page lifetime.
Contract
- The ref starts at
store.get()and is written only fromsubscribe. onScopeDisposeunsubscribes when the owning effect scope stops.- The returned ref is read-only; controls stay on the
Stopwatch.
Re-render cost
elapsed is raw milliseconds delivered at the clock's tick cadence, so anything reading the ref re-renders roughly 60 times a second under createBrowserClock. Pass precisionMs to coarsen notifies — see Options. Keep the elapsed read in a small component when you still want finer UI.