Rating Group
Rating group allows a user to assign rating to an item within a product.
Features
- Support for syncing
disabled
state of withfieldset
element - Support for form
reset
events
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/vue # or yarn add @zag-js/rating-group @zag-js/vue
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/solid # or yarn add @zag-js/rating-group @zag-js/solid
This command will install the framework agnostic rating logic and the reactive utilities for your framework of choice.
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-part
attribute to help identify them in the DOM.
On a high level, the rating consists of:
- Root: The root container for the rating
- Label: The label that gives the user information on the rating
- Item: The element that visually represents the each rating item.
- Item Group: The radiogroup wrapper for the rating items.
- Input: The native html input that is visually hidden in the rating.
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
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 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 [state, send] = useMachine(rating.machine({ id: "1" })) const api = rating.connect(state, send, normalizeProps) return ( <div {...api.rootProps}> <label {...api.labelProps}>Rate:</label> <div {...api.controlProps}> {api.sizeArray.map((index) => { const state = api.getRatingState(index) return ( <span key={index} {...api.getRatingProps({ index })}> {state.isHalf ? <HalfStar /> : <Star />} </span> ) })} </div> <input {...api.hiddenInputProps} /> </div> ) }
import * as rating from "@zag-js/rating-group" import { normalizeProps, useMachine } from "@zag-js/vue" import { defineComponent, h, Fragment, computed } from "vue" import { HalfStar, Star } from "./icons" export default function defineComponent({ name: "Rating" setup() { const [state, send] = useMachine(rating.machine({ id: "rating" })) const apiRef = computed(() => rating.connect(state.value, send, normalizeProps), ) return () => { const api = apiRef.value return ( <div {...api.rootProps}> <label {...api.labelProps}>Rate:</label> <div {...api.controlProps}> {api.sizeArray.map((index) => { const state = api.getRatingState(index) return ( <span key={index} {...api.getRatingProps({ index })}> {state.isHalf ? <HalfStar /> : <Star />} </span> ) })} </div> <input {...api.hiddenInputProps} /> </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 [state, send] = useMachine(rating.machine({ id: "1" })) const api = computed(() => rating.connect(state.value, send, normalizeProps)) </script> <template> <div v-bind="api.rootProps"> <label v-bind="api.labelProps">Rate:</label> <div v-bind="api.controlProps"> <span v-for="index in api.sizeArray" :key="index" v-bind="api.getRatingProps({ index })" > <HalfStar v-if="api.getRatingState(index).isHalf" /> <Star v-else /> </span> </div> <input {...api.hiddenInputProps} /> </div> </template>
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 [state, send] = useMachine(rating.machine({ id: createUniqueId() })) const api = createMemo(() => rating.connect(state, send, normalizeProps)) return ( <div {...api().rootProps}> <label {...api().labelProps}>Rate:</label> <div {...api().controlProps}> {api().sizeArray.map((index) => { const state = api().getRatingState(index) return ( <span key={index} {...api().getRatingProps({ index })}> {state.isHalf ? <HalfStar /> : <Star />} </span> ) })} </div> <input {...api().hiddenInputProps} /> </div> ) }
Disabling the rating group
To make rating disabled, set the context's disabled
property to true
const [state, send] = useMachine( rating.machine({ disabled: true, }), )
Making the rating readonly
To make rating readonly, set the context's readOnly
property to true
const [state, send] = useMachine( rating.machine({ readOnly: true, }), )
Setting the initial value
To set the rating's initial value, set the context's value
property.
const [state, send] = useMachine( rating.machine({ value: 2.5, }), )
Listening for changes
When the rating value changes, the onChange
callback is invoked.
const [state, send] = useMachine( rating.machine({ onChange({ 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 [state, send] = 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
The rating's api
provides helpful properties and methods
setValue
(value: number) => void
Sets the value of the rating groupclearValue
() => void
Clears the value of the rating groupisHovering
boolean
Whether the rating group is being hoveredvalue
number
The current value of the rating grouphoveredValue
number
The value of the currently hovered ratingsize
number
The maximum value of the rating groupsizeArray
number[]
The array of rating values. Returns an array of numbers from 1 to the max value.getRatingState
(props: ItemState
Returns the state of a rating item
Edit this page on GitHub