Rating Group
Rating group allows a user to assign rating to an item within a product.
Features
- Support for syncing
disabledstate of withfieldsetelement - Support for form
resetevents
Installation
To use the rating group machine in your project, run the following command in your command line:
npm install @zag-js/rating-group @zag-js/react # or yarn add @zag-js/rating-group @zag-js/react
npm install @zag-js/rating-group @zag-js/solid # or yarn add @zag-js/rating-group @zag-js/solid
npm install @zag-js/rating-group @zag-js/vue # or yarn add @zag-js/rating-group @zag-js/vue
npm install @zag-js/rating-group @zag-js/svelte # or yarn add @zag-js/rating-group @zag-js/svelte
Anatomy
To set up the rating 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 rating package into your project
import * as rating from "@zag-js/rating-group"
The rating package exports two key functions:
machine— The state machine logic for the rating 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 rating machine in your project 🔥
import * as rating from "@zag-js/rating-group" import { useMachine, normalizeProps } from "@zag-js/react" import { HalfStar, Star } from "./icons" function Rating() { const service = useMachine(rating.machine, { id: "1" }) const api = rating.connect(service, normalizeProps) return ( <div {...api.getRootProps()}> <label {...api.getLabelProps()}>Rate:</label> <div {...api.getControlProps()}> {api.items.map((index) => { const state = api.getItemState({ index }) return ( <span key={index} {...api.getItemProps({ index })}> {state.half ? <HalfStar /> : <Star />} </span> ) })} </div> <input {...api.getHiddenInputProps()} /> </div> ) }
import * as rating from "@zag-js/rating-group" import { useMachine, normalizeProps } from "@zag-js/solid" import { createMemo, createUniqueId } from "solid-js" import { HalfStar, Star } from "./icons" function Rating() { const service = useMachine(rating.machine, { id: createUniqueId() }) const api = createMemo(() => rating.connect(service, normalizeProps)) return ( <div {...api().getRootProps()}> <label {...api().getLabelProps()}>Rate:</label> <div {...api().getControlProps()}> {api().items.map((index) => { const state = api().getItemState(index) return ( <span key={index} {...api().getItemProps({ index })}> {state.half ? <HalfStar /> : <Star />} </span> ) })} </div> <input {...api().getHiddenInputProps()} /> </div> ) }
<script setup> import * as rating from "@zag-js/rating-group" import { normalizeProps, useMachine } from "@zag-js/vue" import { computed } from "vue" import { HalfStar, Star } from "./icons" const service = useMachine(rating.machine, { id: "1" }) const api = computed(() => rating.connect(service, normalizeProps)) </script> <template> <div v-bind="api.getRootProps()"> <label v-bind="api.getLabelProps()">Rate:</label> <div v-bind="api.getControlProps()"> <span v-for="index in api.items" :key="index" v-bind="api.getItemProps({ index })" > <HalfStar v-if="api.getItemState({ index }).half" /> <Star v-else /> </span> </div> <input v-bind="api.getHiddenInputProps()" /> </div> </template>
<script lang="ts"> import * as rating from "@zag-js/rating-group" import { normalizeProps, useMachine } from "@zag-js/svelte" const id = $props.id() const service = useMachine(rating.machine, { id, defaultValue: 2.5, }) const api = $derived(rating.connect(service, normalizeProps)) </script> <div {...api.getRootProps()}> <label {...api.getLabelProps()}>Rate:</label> <div {...api.getControlProps()}> {#each api.items as index} {@const snapshot = api.getItemState({ index })} <span {...api.getItemProps({ index })}> {#if snapshot.half} {@render HalfStar()} {:else} {@render Star()} {/if} </span> {/each} </div> <input {...api.getHiddenInputProps()} data-testid="hidden-input" /> </div>
Disabling the rating group
To make rating disabled, set the context's disabled property to true
const service = useMachine(rating.machine, { disabled: true, })
Making the rating readonly
To make rating readonly, set the context's readOnly property to true
const service = useMachine(rating.machine, { readOnly: true, })
Setting the initial value
Use the defaultValue property to set the rating's initial value.
const service = useMachine(rating.machine, { defaultValue: 2.5, })
Listening for changes
When the rating value changes, the onValueChange callback is invoked.
const service = useMachine(rating.machine, { onValueChange({ value }) { console.log("rating value is:", value) // '1' | '2.5' | '4' }, })
Usage within forms
To use rating within forms, use the exposed inputProps from the connect
function and ensure you pass name value to the machine's context. It will
render a hidden input and ensure the value changes get propagated to the form
correctly.
const service = useMachine(rating.machine, { name: "rating", })
Styling guide
Earlier, we mentioned that each rating part has a data-part attribute added to
them to select and style them in the DOM.
Disabled State
When the rating is disabled, the data-disabled attribute is added to the
rating, control and label parts.
[data-part="rating"][data-disabled] { /* styles for rating disabled state */ } [data-part="label"][data-disabled] { /* styles for rating control disabled state */ } [data-part="input"][data-disabled] { /* styles for rating label disabled state */ }
Checked State
When the rating is checked, the data-checked attribute is added to the rating
part.
[data-part="rating"][data-checked] { /* styles for rating checked state */ }
Readonly State
When the rating is readonly, the data-readonly attribute is added to the
rating part.
[data-part="rating"][data-readonly] { /* styles for rating readonly state */ }
Highlighted
When a rating is highlighted, the data-highlighted attribute is added to the
rating part.
[data-part="rating"][data-highlighted] { /* styles for highlighted rating */ }
Half rating
When a rating is half, the data-half attribute is added to the rating part.
[data-part="rating"][data-half] { /* styles for half rating */ }
Methods and Properties
Machine Context
The rating group machine exposes the following context properties:
idsPartial<{ root: string; label: string; hiddenInput: string; control: string; item: (id: string) => string; }>The ids of the elements in the rating. Useful for composition.translationsIntlTranslationsSpecifies the localized strings that identifies the accessibility elements and their statescountnumberThe total number of ratings.namestringThe name attribute of the rating element (used in forms).formstringThe associate form of the underlying input element.valuenumberThe controlled value of the ratingdefaultValuenumberThe initial value of the rating when rendered. Use when you don't need to control the value of the rating.readOnlybooleanWhether the rating is readonly.disabledbooleanWhether the rating is disabled.requiredbooleanWhether the rating is required.allowHalfbooleanWhether to allow half stars.autoFocusbooleanWhether to autofocus the rating.onValueChange(details: ValueChangeDetails) => voidFunction to be called when the rating value changes.onHoverChange(details: HoverChangeDetails) => voidFunction to be called when the rating value is hovered.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.
Machine API
The rating group api exposes the following methods:
setValue(value: number) => voidSets the value of the rating groupclearValueVoidFunctionClears the value of the rating grouphoveringbooleanWhether the rating group is being hoveredvaluenumberThe current value of the rating grouphoveredValuenumberThe value of the currently hovered ratingcountnumberThe total number of ratingsitemsnumber[]The array of rating values. Returns an array of numbers from 1 to the max value.getItemState(props: ItemProps) => ItemStateReturns the state of a rating item
Data Attributes
Accessibility
Keyboard Interactions
- ArrowRightMoves focus to the next star, increasing the rating value based on the `allowHalf` property.
- ArrowLeftMoves focus to the previous star, decreasing the rating value based on the `allowHalf` property.
- EnterSelects the focused star in the rating group.
Edit this page on GitHub