Circular Progress
Circular progress is a circular progress bar that can be used to show the progress of a task such as downloading a file, uploading an image, etc.
Features
- Support for minimum and maximum values
- Support for indeterminate progress bars
Installation
To use the progress machine in your project, run the following command in your command line:
npm install @zag-js/progress @zag-js/react # or yarn add @zag-js/progress @zag-js/react
npm install @zag-js/progress @zag-js/solid # or yarn add @zag-js/progress @zag-js/solid
npm install @zag-js/progress @zag-js/vue # or yarn add @zag-js/progress @zag-js/vue
npm install @zag-js/progress @zag-js/svelte # or yarn add @zag-js/progress @zag-js/svelte
Anatomy
To set up the progress correctly, you'll need to understand its anatomy and how we name its parts.
Each part includes a
data-partattribute to help identify them in the DOM.
Usage
First, import the progress package into your project
import * as progress from "@zag-js/progress"
The progress package exports two key functions:
machine— The state machine logic for the progress widget.connect— The function that translates the machine's state to JSX attributes and event handlers.
You'll also need to provide a unique
idto theuseMachinehook. 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 progress machine in your project 🔥
import * as progress from "@zag-js/progress" import { normalizeProps, useMachine } from "@zag-js/react" import { useId } from "react" function Progress() { const service = useMachine(progress.machine, { id: useId() }) const api = progress.connect(service, normalizeProps) return ( <div {...api.getRootProps()}> <div {...api.getLabelProps()}>Upload progress</div> <svg {...api.getCircleProps()}> <circle {...api.getCircleTrackProps()} /> <circle {...api.getCircleRangeProps()} /> </svg> </div> ) }
import * as progress from "@zag-js/progress" import { normalizeProps, useMachine } from "@zag-js/solid" import { createMemo, createUniqueId } from "solid-js" function Progress() { const service = useMachine(progress.machine, { id: useId() }) const api = createMemo(() => progress.connect(service, normalizeProps)) return ( <div {...api().getRootProps()}> <div {...api().getLabelProps()}>Upload progress</div> <svg {...api().getCircleProps()}> <circle {...api().getCircleTrackProps()} /> <circle {...api().getCircleRangeProps()} /> </svg> </div> ) }
<script setup> import * as progress from "@zag-js/progress" import { normalizeProps, useMachine } from "@zag-js/vue" import { computed } from "vue" const service = useMachine(progress.machine, { id: "1" }) const api = computed(() => progress.connect(service, normalizeProps)) </script> <template> <div v-bind="api.getRootProps()"> <div v-bind="api.getLabelProps()">Upload progress</div> <svg v-bind="api.getCircleProps()"> <circle v-bind="api.getCircleTrackProps()" /> <circle v-bind="api.getCircleRangeProps()" /> </svg> </div> </template>
<script lang="ts"> import * as progress from "@zag-js/progress" import { normalizeProps, useMachine } from "@zag-js/svelte" const service = useMachine(progress.machine, { id: "1", }) const api = $derived(progress.connect(service, normalizeProps)) </script> <div {...api.getRootProps()}> <div {...api.getLabelProps()}>Upload progress</div> <svg {...api.getCircleProps()}> <circle {...api.getCircleTrackProps()} /> <circle {...api.getCircleRangeProps()} /> </svg> </div>
Setting the value
Pass the defaultValue or value property to the machine function to set the
initial value of the progress bar.
const service = useMachine(progress.machine, { defaultValue: 50, })
Subsequently, you can use the api.setValue method to set the value of the
progress bar.
api.setValue(50)
Setting the minimum and maximum values
By default, the progress bar has a minimum value of 0 and a maximum value of
100. You can change these values by passing the min and max options to the
machine.
const service = useMachine(progress.machine, { min: 0, max: 1000, })
Using the indeterminate state
The progress component is determinate by default, with the value and max set to
50 and 100 respectively.
Set value to null to indicate an indeterminate value for operations whose
progress can't be determined (e.g., attempting to reconnect to a server).
const service = useMachine(progress.machine, { defaultValue: null, })
Showing a value text
Progress bars can only be interpreted by sighted users. To include a text
description to support assistive technologies like screen readers, use the
valueText part.
const service = useMachine(progress.machine, { translations: { valueText: ({ value, max }) => value == null ? "Loading..." : `${value} of ${max} items loaded`, }, })
Setting the size of the progress bar
Use the --size CSS variable to set the size of the progress bar.
[data-scope="progress"][data-part="circle"] { --size: 400px; }
Setting the thickness of the progress bar
Use the --thickness CSS variable to set the size of the progress bar.
[data-scope="progress"][data-part="circle"] { --thickness: 4px; }
Then you need to render the valueText part in your component.
<div {...api.getValueTextProps()}>{api.valueAsString}</div>
Styling guide
Earlier, we mentioned that each menu part has a data-part attribute added to
them to select and style them in the DOM.
[data-scope="progress"][data-part="root"] { /* Styles for the root part */ } [data-scope="progress"][data-part="circle-track"] { /* Styles for the track part */ } [data-scope="progress"][data-part="circle-range"] { /* Styles for the range part */ }
Indeterminate state
To style the indeterminate state, you can use the [data-state=indeterminate]
selector.
[data-scope="progress"][data-part="root"][data-state="indeterminate"] { /* Styles for the root indeterminate state */ } [data-scope="progress"][data-part="circle-track"][data-state="indeterminate"] { /* Styles for the root indeterminate state */ } [data-scope="progress"][data-part="circle-range"][data-state="indeterminate"] { /* Styles for the root indeterminate state */ }
Methods and Properties
Machine Context
The progress machine exposes the following context properties:
idsPartial<{ root: string; track: string; label: string; circle: string; }>The ids of the elements in the progress bar. Useful for composition.valuenumberThe controlled value of the progress bar.defaultValuenumberThe initial value of the progress bar when rendered. Use when you don't need to control the value of the progress bar.minnumberThe minimum allowed value of the progress bar.maxnumberThe maximum allowed value of the progress bar.translationsIntlTranslationsThe localized messages to use.onValueChange(details: ValueChangeDetails) => voidCallback fired when the value changes.formatOptionsIntl.NumberFormatOptionsThe options to use for formatting the value.localestringThe locale to use for formatting the value.dir"ltr" | "rtl"The document's text/writing direction.idstringThe unique identifier of the machine.getRootNode() => ShadowRoot | Node | DocumentA root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.orientation"horizontal" | "vertical"The orientation of the element.
Machine API
The progress api exposes the following methods:
valuenumberThe current value of the progress bar.valueAsStringstringThe current value of the progress bar as a string.setValue(value: number) => voidSets the current value of the progress bar.setToMaxVoidFunctionSets the current value of the progress bar to the max value.setToMinVoidFunctionSets the current value of the progress bar to the min value.percentnumberThe percentage of the progress bar's value.percentAsStringstringThe percentage of the progress bar's value as a string.minnumberThe minimum allowed value of the progress bar.maxnumberThe maximum allowed value of the progress bar.indeterminatebooleanWhether the progress bar is indeterminate.
Data Attributes
Edit this page on GitHub