Watchstop
00:00.00
Frameworks

Svelte

@watchstop/svelte adapter.

Adapter exposing Store as a Svelte readable store so $ auto-subscription works. @watchstop/core is a peer dependency — install both.

Exact public names

createStopwatch is the entire public API.

type CreateStopwatchOptions =
  | { clock?: Clock; precisionMs?: number }
  | { stopwatch: Stopwatch }

type StopwatchStore = Readable<number> & {
  running: Readable<boolean>
  start: () => void
  stop: () => void
  reset: () => void
  stopwatch: Stopwatch
}

declare function createStopwatch(options?: CreateStopwatchOptions): StopwatchStore

It is named createStopwatch, not fromStore or toStore. svelte/store already exports both of those for converting between Svelte stores and runes.

createStopwatch

createStopwatch owns a Stopwatch and returns the Svelte custom-store shape: a subscribe for $ auto-subscription plus callable controls on the same object.

<script lang="ts">
  import { createStopwatch } from '@watchstop/svelte'

  const stopwatch = createStopwatch()
  const running = stopwatch.running
</script>

<p>{$stopwatch} ms</p>
<button onclick={$running ? stopwatch.stop : stopwatch.start}>
  {$running ? 'Stop' : 'Start'}
</button>
<button onclick={stopwatch.reset}>Reset</button>
  • Construction is inert. Nothing is scheduled until start().
  • The component <script> runs once, so start, stop, and reset are bound once and keep the same identity for the life of the component.
  • subscribe calls the listener synchronously with the current value before registering it, as Svelte's readable contract requires.
  • stopwatch.stopwatch is the owned instance, exposed for passing elsewhere.

Options

OptionTypePurpose
clockClockOwned mode: use this clock instead of detectClock(). Pass createMockClock() in tests.
precisionMsnumberOwned mode: coarsen notify cadence — see Options.
stopwatchStopwatchBorrowed mode: bind this instance; do not pass clock / precisionMs.

Teardown is tied to the component, not to subscriber count

createStopwatch registers onDestroy, so destroy() runs when the component that created the store is destroyed.

It deliberately does not destroy on last-unsubscribe, which is what svelte/store's readable start/stop notifier does. A Svelte store may be subscribed and unsubscribed many times during one component's life — {#if} blocks and conditional {$stopwatch} reads do exactly that — and a stopwatch is controlled imperatively, so it can legitimately be running with zero subscribers. Destroying at subscriber zero would silently kill a running stopwatch and leave a dead instance behind for the next subscriber. Component lifetime is the only boundary that matches who owns the instance.

Calling createStopwatch() outside component initialisation — at module level, for a singleton — is allowed and simply registers no automatic teardown. Call stopwatch.stopwatch.destroy() yourself in that case.

Borrowed mode never calls destroy() on the shared instance.

Sharing one stopwatch across components

Pass the same core instance into each store:

<script lang="ts">
  import { Stopwatch } from '@watchstop/core'
  import { createStopwatch } from '@watchstop/svelte'

  const session = new Stopwatch()
  const stopwatch = createStopwatch({ stopwatch: session })
  const running = stopwatch.running
</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

  • subscribe calls the listener synchronously with the current elapsed value before registering it, as Svelte's readable contract requires.
  • The returned unsubscribe function is the underlying Stopwatch.subscribe's own, so $ auto-subscription cleanup unsubscribes from the instance.
  • The Readable half is value-only; controls are callable properties on the same object.

Re-render cost

$stopwatch is raw milliseconds delivered at the clock's tick cadence, so the markup reading it updates roughly 60 times a second under createBrowserClock. Pass precisionMs to coarsen notifies — see Options. Keep the $stopwatch read in a small component when you still want finer UI.

See Store and Spec.

On this page