Timer
The timer machine is used to record the time elapsed from zero or since a specified target time.
Features
- Countdown from a specified time.
- Use as stopwatch to record the time elapsed.
- Control the timer with start, stop, and resume buttons.
- Set the tick interval for the timer.
Installation
To use the Timer machine in your project, run the following command in your command line:
npm install @zag-js/timer @zag-js/react # or yarn add @zag-js/timer @zag-js/react
npm install @zag-js/timer @zag-js/vue # or yarn add @zag-js/timer @zag-js/vue
npm install @zag-js/timer @zag-js/vue # or yarn add @zag-js/timer @zag-js/vue
npm install @zag-js/timer @zag-js/solid # or yarn add @zag-js/timer @zag-js/solid
Anatomy
To set up the Timer correctly, you'll need to understand its anatomy and how we name its parts.
Each part includes a
data-part
attribute to help identify them in the DOM.
Usage
First, import the timer package into your project
import * as timer from "@zag-js/timer"
The Timer package exports these functions:
machine
— The state machine logic for the Time Picker widget.connect
— The function that translates the machine's state to JSX attributes and event handlers.parse
— The function to parse a date time string or object into aTime
object.
You'll also need to provide a unique
id
to theuseMachine
hook. This is used to ensure that every part has a unique identifier.
Next, import the required hooks and functions for your framework and use the Time Picker machine in your project 🔥
import { normalizeProps, useMachine } from "@zag-js/react" import * as timer from "@zag-js/timer" export function Countdown() { const [state, send] = useMachine( timer.machine({ id: useId(), countdown: true, autoStart: true, startMs: timer.parse({ days: 2, seconds: 10 }), onComplete() { console.log("Timer completed") }, }), ) const api = timer.connect(state, send, normalizeProps) return ( <div> <div {...api.getRootProps()}> <div {...api.getItemProps({ type: "days" })}> {api.formattedTime.days} </div> <div {...api.getSeparatorProps()}>:</div> <div {...api.getItemProps({ type: "hours" })}> {api.formattedTime.hours} </div> <div {...api.getSeparatorProps()}>:</div> <div {...api.getItemProps({ type: "minutes" })}> {api.formattedTime.minutes} </div> <div {...api.getSeparatorProps()}>:</div> <div {...api.getItemProps({ type: "seconds" })}> {api.formattedTime.seconds} </div> </div> <div style={{ display: "flex", gap: "4px" }}> <button onClick={api.start}>START</button> <button onClick={api.pause}>PAUSE</button> <button onClick={api.resume}>RESUME</button> <button onClick={api.reset}>RESET</button> </div> </div> ) }
import * as timer from "@zag-js/timer" import { useMachine, normalizeProps } from "@zag-js/vue" import { computed, defineComponent } from "vue" export default defineComponent({ name: "TimerCountdown", setup() { const [state, send] = useMachine( timer.machine({ id: "v1", countdown: true, autoStart: true, startMs: timer.parse({ days: 2, seconds: 10 }), onComplete() { console.log("Timer completed") }, }), ) const apiRef = computed(() => timer.connect(state.value, send, normalizeProps), ) return () => { const api = apiRef.value return ( <div> <div {...api.getRootProps()}> <div {...api.getItemProps({ type: "days" })}> {api.formattedTime.days} </div> <div {...api.getSeparatorProps()}>:</div> <div {...api.getItemProps({ type: "hours" })}> {api.formattedTime.hours} </div> <div {...api.getSeparatorProps()}>:</div> <div {...api.getItemProps({ type: "minutes" })}> {api.formattedTime.minutes} </div> <div {...api.getSeparatorProps()}>:</div> <div {...api.getItemProps({ type: "seconds" })}> {api.formattedTime.seconds} </div> </div> <div style={{ display: "flex", gap: "4px" }}> <button onClick={api.start}>START</button> <button onClick={api.pause}>PAUSE</button> <button onClick={api.resume}>RESUME</button> <button onClick={api.reset}>RESET</button> </div> </div> ) } }, })
<script setup lang="ts"> import * as timer from "@zag-js/timer" import { normalizeProps, useMachine } from "@zag-js/vue" import { computed } from "vue" const [state, send] = useMachine( timer.machine({ id: "v1", countdown: true, autoStart: true, startMs: timer.parse({ days: 2, seconds: 10 }), onComplete() { console.log("Timer completed") }, }), ) const api = computed(() => timer.connect(state.value, send, normalizeProps)) </script> <template> <div> <div v-bind="api.getRootProps()"> <div v-bind="api.getItemProps({ type: 'days' })"> {{ api.formattedTime.days }} </div> <div v-bind="api.getSeparatorProps()">:</div> <div v-bind="api.getItemProps({ type: 'hours' })"> {{ api.formattedTime.hours }} </div> <div v-bind="api.getSeparatorProps()">:</div> <div v-bind="api.getItemProps({ type: 'minutes' })"> {{ api.formattedTime.minutes }} </div> <div v-bind="api.getSeparatorProps()">:</div> <div v-bind="api.getItemProps({ type: 'seconds' })"> {{ api.formattedTime.seconds }} </div> </div> <div style="display: flex; gap: 4px"> <button @click="api.start">START</button> <button @click="api.pause">PAUSE</button> <button @click="api.resume">RESUME</button> <button @click="api.reset">RESET</button> </div> </div> </template>
import { normalizeProps, useMachine } from "@zag-js/solid" import * as timer from "@zag-js/timer" import { createMemo, createUniqueId } from "solid-js" export default function Page() { const [state, send] = useMachine( timer.machine({ id: createUniqueId(), countdown: true, autoStart: true, startMs: timer.parse({ days: 2, seconds: 10 }), onComplete() { console.log("Timer completed") }, }), ) const api = createMemo(() => timer.connect(state, send, normalizeProps)) return ( <div> <div {...api().getRootProps()}> <div {...api().getItemProps({ type: "days" })}> {api().formattedTime.days} </div> <div {...api().getSeparatorProps()}>:</div> <div {...api().getItemProps({ type: "hours" })}> {api().formattedTime.hours} </div> <div {...api().getSeparatorProps()}>:</div> <div {...api().getItemProps({ type: "minutes" })}> {api().formattedTime.minutes} </div> <div {...api().getSeparatorProps()}>:</div> <div {...api().getItemProps({ type: "seconds" })}> {api().formattedTime.seconds} </div> </div> <div style={{ display: "flex", gap: "4px" }}> <button onClick={api().start}>START</button> <button onClick={api().pause}>PAUSE</button> <button onClick={api().resume}>RESUME</button> <button onClick={api().reset}>RESET</button> </div> </div> ) }
Setting the start value
Set the startMs
property to the timer machine's context to set the start time
in milliseconds.
const [state, send] = useMachine( timer.machine({ startMs: 1000 * 60 * 60, // 1 hour }), )
Alternatively, you can also use the timer.parse
function to convert a date
time string or object into milliseconds
const [state, send] = useMachine( timer.machine({ startMs: timer.parse("2021-01-01T12:00:00Z"), // startMs: timer.parse({ hours: 12, minutes: 0, seconds: 0 }), }), )
Auto starting the timer
Set the autoStart
property to true
in the timer machine's context to start
the timer automatically when the component mounts.
const [state, send] = useMachine( timer.machine({ autoStart: true, }), )
Usage as countdown timer
To use the timer as a countdown timer, set the countdown
property to true
in
the timer machine's context.
const [state, send] = useMachine( timer.machine({ countdown: true, }), )
Setting the target value
To set the target value of the countdown timer, pass the targetMs
property in
the timer machine's context. The timer stops automatically when the targetMs
is reached.
When targetMs
is set and countdown=true
, the timer ticks down to zero from
the specified target time.
const [state, send] = useMachine( timer.machine({ countdown: true, targetMs: 1000 * 60 * 60, // 1 hour }), )
When targetMs
is set and countdown=false|undefined
, the timer ticks up to
the specified target time.
const [state, send] = useMachine( timer.machine({ targetMs: 1000 * 60 * 60, // 1 hour }), )
Setting the tick interval
Set the interval
property to the timer machine's context to set the tick
interval in milliseconds.
const [state, send] = useMachine( timer.machine({ interval: 1000, // 1 second }), )
Listening to tick events
When the timer ticks, the onTick
callback is invoke. You can listen to this
event and update your UI accordingly.
const [state, send] = useMachine( timer.machine({ onTick(details) { // details => { value, segments } console.log(details) }, }), )
Listening for completion events
When the timer reaches the target time, the onComplete
callback is invoked.
const [state, send] = useMachine( timer.machine({ countdown: true, targetMs: 1000 * 60 * 60, // 1 hour onComplete() { console.log("Timer completed") }, }), )
Starting and Stopping the timer
To start the timer, send the api.start()
callback
api.start()
To stop the timer, send the api.stop()
callback
api.stop()
Pausing and Resuming the timer
To pause the timer, send the api.pause()
callback
api.pause()
To resume the timer, send the api.resume()
callback
api.resume()
Methods and Properties
Machine Context
The time picker machine exposes the following context properties:
countdown
boolean
Whether the timer should countdown, decrementing the timer on each tick.startMs
number
The total duration of the timer in milliseconds.targetMs
number
The minimum count of the timer in milliseconds.autoStart
boolean
Whether the timer should start automaticallyinterval
number
The interval in milliseconds to update the timer count.onTick
(details: TickDetails) => void
Function invoked when the timer ticksonComplete
() => void
Function invoked when the timer is completedid
string
The unique identifier of the machine.getRootNode
() => ShadowRoot | Node | Document
A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
Machine API
The time picker api
exposes the following methods:
running
boolean
Whether the timer is running.paused
boolean
Whether the timer is paused.time
Time<number>
The formatted timer count value.formattedTime
Time<string>
The formatted time parts of the timer count.start
() => void
Function to start the timer.pause
() => void
Function to pause the timer.resume
() => void
Function to resume the timer.reset
() => void
Function to reset the timer.restart
() => void
Function to restart the timer.progressPercent
number
The progress percentage of the timer.
Edit this page on GitHub