# Rating Group

URL: https://ark-ui.com/docs/components/rating-group
Source: https://raw.githubusercontent.com/chakra-ui/ark/refs/heads/main/website/src/content/pages/components/rating-group.mdx

Allows users to rate items using a set of icons.

---

<ComponentPreview id="RatingGroup" />

## Anatomy

<Anatomy id="rating-group" />

```tsx
<RatingGroup.Root>
  <RatingGroup.Label />
  <RatingGroup.Control>
    <RatingGroup.Item>
      <RatingGroup.ItemContext />
    </RatingGroup.Item>
    <RatingGroup.HiddenInput />
  </RatingGroup.Control>
</RatingGroup.Root>
```

## Examples

**Example: basic**

```ripple
import { RatingGroup } from 'ark-ripple/rating-group';
import { Star } from 'lucide-ripple';
import styles from 'styles/rating-group.module.css';

export component Basic() {
  <RatingGroup.Root class={styles.Root} defaultValue={3}>
    <RatingGroup.Label class={styles.Label}>{'Label'}</RatingGroup.Label>
    <RatingGroup.Control class={styles.Control}>
      <RatingGroup.Context>
        component children({ context }) {
          for (const item of @context.items; key item) {
            <RatingGroup.Item class={styles.Item} index={item}>
              <RatingGroup.ItemContext>
                component children({ context }) {
                  <span
                    class={styles.ItemIndicator}
                    data-highlighted={@context.highlighted ? '' : undefined}
                  >
                    <Star data-bg="" />
                    <Star data-fg="" fill="currentColor" />
                  </span>
                }
              </RatingGroup.ItemContext>
            </RatingGroup.Item>
          }
        }
      </RatingGroup.Context>
      <RatingGroup.HiddenInput />
    </RatingGroup.Control>
  </RatingGroup.Root>
}

```

### Controlled

When using the `RatingGroup` component, you can use the `value` and `onValueChange` props to control the state.

**Example: controlled**

```ripple
import { RatingGroup } from 'ark-ripple/rating-group';
import { Star } from 'lucide-ripple';
import { track } from 'ripple';
import styles from 'styles/rating-group.module.css';

export component Controlled() {
  let value = track(0);

  <RatingGroup.Root
    class={styles.Root}
    {value}
    onValueChange={(details) => {
      @value = details.value;
    }}
  >
    <RatingGroup.Label class={styles.Label}>{'Label'}</RatingGroup.Label>
    <RatingGroup.Control class={styles.Control}>
      <RatingGroup.Context>
        component children({ context }) {
          for (const item of @context.items; key item) {
            <RatingGroup.Item class={styles.Item} index={item}>
              <RatingGroup.ItemContext>
                component children({ context }) {
                  <span
                    class={styles.ItemIndicator}
                    data-half={@context.half ? '' : undefined}
                    data-highlighted={@context.highlighted ? '' : undefined}
                  >
                    <Star data-bg="" />
                    <Star data-fg="" fill="currentColor" />
                  </span>
                }
              </RatingGroup.ItemContext>
            </RatingGroup.Item>
          }
        }
      </RatingGroup.Context>
      <RatingGroup.HiddenInput />
    </RatingGroup.Control>
  </RatingGroup.Root>
}

```

### Root Provider

An alternative way to control the rating group is to use the `RootProvider` component and the `useRatingGroup` hook.
This way you can access the state and methods from outside the component.

**Example: root-provider**

```ripple
import { RatingGroup, useRatingGroup } from 'ark-ripple/rating-group';
import { Star } from 'lucide-ripple';
import styles from 'styles/rating-group.module.css';

export component RootProvider() {
  const ratingGroup = useRatingGroup({ count: 5, defaultValue: 3 });

  <div class="stack">
    <output>
      {'value: '}
      {@ratingGroup.value}
    </output>
    <RatingGroup.RootProvider class={styles.Root} value={ratingGroup}>
      <RatingGroup.Label class={styles.Label}>{'Label'}</RatingGroup.Label>
      <RatingGroup.Control class={styles.Control}>
        <RatingGroup.Context>
          component children({ context }) {
            for (const item of @context.items; key item) {
              <RatingGroup.Item class={styles.Item} index={item}>
                <RatingGroup.ItemContext>
                  component children({ context }) {
                    <span
                      class={styles.ItemIndicator}
                      data-highlighted={@context.highlighted ? '' : undefined}
                    >
                      <Star data-bg="" />
                      <Star data-fg="" fill="currentColor" />
                    </span>
                  }
                </RatingGroup.ItemContext>
              </RatingGroup.Item>
            }
          }
        </RatingGroup.Context>
        <RatingGroup.HiddenInput />
      </RatingGroup.Control>
    </RatingGroup.RootProvider>
  </div>
}

```

### Field

The `Field` component helps manage form-related state and accessibility attributes of a rating group. It includes
handling ARIA labels, helper text, and error text to ensure proper accessibility.

**Example: with-field**

```ripple
import { Field } from 'ark-ripple/field';
import { RatingGroup } from 'ark-ripple/rating-group';
import { Star } from 'lucide-ripple';
import field from 'styles/field.module.css';
import styles from 'styles/rating-group.module.css';

export component WithField() {
  <Field.Root class={field.Root}>
    <RatingGroup.Root class={styles.Root} count={5} defaultValue={3}>
      <RatingGroup.Label class={styles.Label}>{'Label'}</RatingGroup.Label>
      <RatingGroup.Control class={styles.Control}>
        <RatingGroup.Context>
          component children({ context }) {
            for (const item of @context.items; key item) {
              <RatingGroup.Item class={styles.Item} index={item}>
                <RatingGroup.ItemContext>
                  component children({ context }) {
                    <span
                      class={styles.ItemIndicator}
                      data-highlighted={@context.highlighted ? '' : undefined}
                    >
                      <Star data-bg="" />
                      <Star data-fg="" fill="currentColor" />
                    </span>
                  }
                </RatingGroup.ItemContext>
              </RatingGroup.Item>
            }
          }
        </RatingGroup.Context>
        <RatingGroup.HiddenInput />
      </RatingGroup.Control>
    </RatingGroup.Root>
    <Field.HelperText class={field.HelperText}>{'Additional Info'}</Field.HelperText>
    <Field.ErrorText class={field.ErrorText}>{'Error Info'}</Field.ErrorText>
  </Field.Root>
}

```

### Half Rating

Allow `0.5` value steps by setting the `allowHalf` prop to `true`. Ensure to render the correct icon if the `half` value
is set in the Rating components render callback.

**Example: half-star**

```ripple
import { RatingGroup } from 'ark-ripple/rating-group';
import { Star } from 'lucide-ripple';
import styles from 'styles/rating-group.module.css';

export component HalfStar() {
  <RatingGroup.Root class={styles.Root} defaultValue={2.5} allowHalf>
    <RatingGroup.Label class={styles.Label}>{'Label'}</RatingGroup.Label>
    <RatingGroup.Control class={styles.Control}>
      <RatingGroup.Context>
        component children({ context }) {
          for (const item of @context.items; key item) {
            <RatingGroup.Item class={styles.Item} index={item}>
              <RatingGroup.ItemContext>
                component children({ context }) {
                  <span
                    class={styles.ItemIndicator}
                    data-half={@context.half ? '' : undefined}
                    data-highlighted={@context.highlighted ? '' : undefined}
                  >
                    <Star data-bg="" />
                    <Star data-fg="" fill="currentColor" />
                  </span>
                }
              </RatingGroup.ItemContext>
            </RatingGroup.Item>
          }
        }
      </RatingGroup.Context>
      <RatingGroup.HiddenInput />
    </RatingGroup.Control>
  </RatingGroup.Root>
}

```

### Forms

To use the rating group within forms, pass the prop `name`. It will render a hidden input and ensure the value changes
get propagated to the form correctly.

**Example: form-usage**

```ripple
import { RatingGroup } from 'ark-ripple/rating-group';
import { Star } from 'lucide-ripple';
import button from 'styles/button.module.css';
import styles from 'styles/rating-group.module.css';

export component FormUsage() {
  <form
    class="stack"
    onSubmit={(e) => {
      e.preventDefault();
      const formData = new FormData(e.currentTarget);
      alert(`Rating value: ${formData.get('review')}`);
    }}
  >
    <RatingGroup.Root class={styles.Root} name="review" defaultValue={3}>
      <RatingGroup.Label class={styles.Label}>{'Label'}</RatingGroup.Label>
      <RatingGroup.Control class={styles.Control}>
        <RatingGroup.Context>
          component children({ context }) {
            for (const item of @context.items; key item) {
              <RatingGroup.Item class={styles.Item} index={item}>
                <RatingGroup.ItemContext>
                  component children({ context }) {
                    <span
                      class={styles.ItemIndicator}
                      data-highlighted={@context.highlighted ? '' : undefined}
                    >
                      <Star data-bg="" />
                      <Star data-fg="" fill="currentColor" />
                    </span>
                  }
                </RatingGroup.ItemContext>
              </RatingGroup.Item>
            }
          }
        </RatingGroup.Context>
        <RatingGroup.HiddenInput />
      </RatingGroup.Control>
    </RatingGroup.Root>
    <button type="submit" class={button.Root}>{'Submit'}</button>
  </form>
}

```

### Disabled

To make the rating group disabled, set the `disabled` prop to `true`.

**Example: disabled**

```ripple
import { RatingGroup } from 'ark-ripple/rating-group';
import { Star } from 'lucide-ripple';
import styles from 'styles/rating-group.module.css';

export component Disabled() {
  <RatingGroup.Root class={styles.Root} defaultValue={3} disabled>
    <RatingGroup.Label class={styles.Label}>{'Label'}</RatingGroup.Label>
    <RatingGroup.Control class={styles.Control}>
      <RatingGroup.Context>
        component children({ context }) {
          for (const item of @context.items; key item) {
            <RatingGroup.Item class={styles.Item} index={item}>
              <RatingGroup.ItemContext>
                component children({ context }) {
                  <span
                    class={styles.ItemIndicator}
                    data-highlighted={@context.highlighted ? '' : undefined}
                  >
                    <Star data-bg="" />
                    <Star data-fg="" fill="currentColor" />
                  </span>
                }
              </RatingGroup.ItemContext>
            </RatingGroup.Item>
          }
        }
      </RatingGroup.Context>
      <RatingGroup.HiddenInput />
    </RatingGroup.Control>
  </RatingGroup.Root>
}

```

## API Reference

### Props

**Component API Reference**

**Root Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `allowHalf` | `boolean` | No | Whether to allow half stars. |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `autoFocus` | `boolean` | No | Whether to autofocus the rating. |
| `count` | `number` | No | The total number of ratings. |
| `defaultValue` | `number` | No | The initial value of the rating when rendered.
Use when you don't need to control the value of the rating. |
| `disabled` | `boolean` | No | Whether the rating is disabled. |
| `form` | `string` | No | The associate form of the underlying input element. |
| `id` | `string` | No | The unique identifier of the machine. |
| `ids` | `Partial<{
  root: string
  label: string
  hiddenInput: string
  control: string
  item: (id: string) => string
}>` | No | The ids of the elements in the rating. Useful for composition. |
| `name` | `string` | No | The name attribute of the rating element (used in forms). |
| `onHoverChange` | `(details: HoverChangeDetails) => void` | No | Function to be called when the rating value is hovered. |
| `onValueChange` | `(details: ValueChangeDetails) => void` | No | Function to be called when the rating value changes. |
| `readOnly` | `boolean` | No | Whether the rating is readonly. |
| `required` | `boolean` | No | Whether the rating is required. |
| `translations` | `IntlTranslations` | No | Specifies the localized strings that identifies the accessibility elements and their states |
| `value` | `number` | No | The controlled value of the rating |


**Control Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Control Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | rating-group |
| `[data-part]` | control |
| `[data-readonly]` | Present when read-only |
| `[data-disabled]` | Present when disabled |


**HiddenInput Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Item Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `index` | `number` | Yes |  |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Item Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | rating-group |
| `[data-part]` | item |
| `[data-disabled]` | Present when disabled |
| `[data-readonly]` | Present when read-only |
| `[data-checked]` | Present when checked |
| `[data-highlighted]` | Present when highlighted |
| `[data-half]` |  |


**Label Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Label Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | rating-group |
| `[data-part]` | label |
| `[data-disabled]` | Present when disabled |
| `[data-required]` | Present when required |


**RootProvider Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `value` | `UseRatingGroupReturn` | Yes |  |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


### Context

**API:**

| Property | Type | Description |
|----------|------|-------------|
| `setValue` | `(value: number) => void` | Sets the value of the rating group |
| `clearValue` | `VoidFunction` | Clears the value of the rating group |
| `hovering` | `boolean` | Whether the rating group is being hovered |
| `value` | `number` | The current value of the rating group |
| `hoveredValue` | `number` | The value of the currently hovered rating |
| `count` | `number` | The total number of ratings |
| `items` | `number[]` | The array of rating values. Returns an array of numbers from 1 to the max value. |
| `getItemState` | `(props: ItemProps) => ItemState` | Returns the state of a rating item |


## Accessibility

### Keyboard Support

<KeyBindingsTable id="rating-group" />