Rating Group
Allows users to rate items using a set of icons.
Allows users to rate items using a set of icons.
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.
Learn how to use the RatingGroup component in your project. Let’s take a look
at the most basic example:
import { RatingGroup } from '@ark-ui/react'
const Basic = () => (
  <RatingGroup.Root count={5} defaultValue={3}>
    <RatingGroup.Label>Label</RatingGroup.Label>
    <RatingGroup.Control>
      {({ items }) =>
        items.map((item) => (
          <RatingGroup.Item key={item} index={item}>
            {({ isHighlighted }) => {
              if (isHighlighted) return <IconFull />
              return <IconEmpty />
            }}
          </RatingGroup.Item>
        ))
      }
    </RatingGroup.Control>
  </RatingGroup.Root>
)
import { RatingGroup } from '@ark-ui/solid'
import { Index } from 'solid-js'
const Basic = () => (
  <RatingGroup.Root count={5} value={3}>
    <RatingGroup.Label>Label</RatingGroup.Label>
    <RatingGroup.Control>
      {(api) => (
        <Index each={api().items}>
          {(index) => (
            <RatingGroup.Item index={index()}>
              {(api) => (api().isHighlighted ? <IconFull /> : <IconEmpty />)}
            </RatingGroup.Item>
          )}
        </Index>
      )}
    </RatingGroup.Control>
  </RatingGroup.Root>
)
<script setup lang="ts">
import { ref } from 'vue'
import { RatingGroup } from '@ark-ui/vue'
import { IconEmpty, IconFull, IconHalf } from './rating-icons'
const value = ref(0)
</script>
<template>
  <RatingGroup.Root :count="5" :model-value="3">
    <RatingGroup.Label>Label</RatingGroup.Label>
    <RatingGroup.Control v-slot="{ items }">
      <RatingGroup.Item v-for="item in items" :key="item" :index="item" v-slot="{ isHighlighted }">
        <IconFull v-if="isHighlighted" />
        <IconEmpty v-else />
      </RatingGroup.Item>
    </RatingGroup.Control>
  </RatingGroup.Root>
</template>
Allow 0.5 value steps by setting the allowHalf prop to true. Ensure to
render the correct icon if the isHalf value is set in the Rating components
render callback.
import { RatingGroup } from '@ark-ui/react'
const HalfRatings = () => (
  <RatingGroup.Root count={5} defaultValue={3} allowHalf>
    <RatingGroup.Label>Label</RatingGroup.Label>
    <RatingGroup.Control>
      {({ items }) =>
        items.map((item) => (
          <RatingGroup.Item key={item} index={item}>
            {({ isHalf, isHighlighted }) => {
              if (isHalf) return <IconHalf />
              if (isHighlighted) return <IconFull />
              return <IconEmpty />
            }}
          </RatingGroup.Item>
        ))
      }
    </RatingGroup.Control>
  </RatingGroup.Root>
)
import { RatingGroup } from '@ark-ui/solid'
import { Index } from 'solid-js'
const HalfRatings = () => (
  <RatingGroup.Root count={5} value={3} allowHalf>
    <RatingGroup.Label>Label</RatingGroup.Label>
    <RatingGroup.Control>
      {(api) => (
        <Index each={api().items}>
          {(index) => (
            <RatingGroup.Item index={index()}>
              {(api) =>
                api().isHalf ? <IconHalf /> : api().isHighlighted ? <IconFull /> : <IconEmpty />
              }
            </RatingGroup.Item>
          )}
        </Index>
      )}
    </RatingGroup.Control>
  </RatingGroup.Root>
)
<script setup lang="ts">
import { ref } from 'vue'
import { RatingGroup } from '@ark-ui/vue'
import { IconEmpty, IconFull, IconHalf } from './rating-icons'
const value = ref(0)
</script>
<template>
  <RatingGroup.Root :count="5" :model-value="3" allowHalf>
    <RatingGroup.Label>Label</RatingGroup.Label>
    <RatingGroup.Control v-slot="{ items }">
      <RatingGroup.Item
        v-for="item in items"
        :key="item"
        :index="item"
        v-slot="{ isHalf, isHighlighted }"
      >
        <IconHalf v-if="isHalf" />
        <IconFull v-else-if="isHighlighted" />
        <IconEmpty v-else />
      </RatingGroup.Item>
    </RatingGroup.Control>
  </RatingGroup.Root>
</template>
import { RatingGroup } from '@ark-ui/react'
const InitialValue = () => (
  <RatingGroup.Root count={5} defaultValue={2} readOnly>
    <RatingGroup.Label>Label</RatingGroup.Label>
    <RatingGroup.Control>
      {({ items }) =>
        items.map((item) => (
          <RatingGroup.Item key={item} index={item}>
            {({ isHighlighted }) => {
              if (isHighlighted) return <IconFull />
              return <IconEmpty />
            }}
          </RatingGroup.Item>
        ))
      }
    </RatingGroup.Control>
  </RatingGroup.Root>
)
import { RatingGroup } from '@ark-ui/solid'
import { Index } from 'solid-js'
const InitialValue = () => (
  <RatingGroup.Root count={5} value={2} readOnly>
    <RatingGroup.Label>Label</RatingGroup.Label>
    <RatingGroup.Control>
      {(api) => (
        <Index each={api().items}>
          {(index) => (
            <RatingGroup.Item index={index()}>
              {(api) =>
                api().isHalf ? <IconHalf /> : api().isHighlighted ? <IconFull /> : <IconEmpty />
              }
            </RatingGroup.Item>
          )}
        </Index>
      )}
    </RatingGroup.Control>
  </RatingGroup.Root>
)
<script setup lang="ts">
import { ref } from 'vue'
import { RatingGroup } from '@ark-ui/vue'
import { IconEmpty, IconFull, IconHalf } from './rating-icons'
const value = ref(0)
</script>
<template>
  <RatingGroup.Root :count="5" :model-value="2" readOnly>
    <RatingGroup.Label>Label</RatingGroup.Label>
    <RatingGroup.Control v-slot="{ items }">
      <RatingGroup.Item v-for="item in items" :key="item" :index="item" v-slot="{ isHighlighted }">
        <IconFull v-if="isHighlighted" />
        <IconEmpty v-else />
      </RatingGroup.Item>
    </RatingGroup.Control>
  </RatingGroup.Root>
</template>
When using the RatingGroup component, you can use the value and
onValueChange props to control the state.
import { RatingGroup } from '@ark-ui/react'
import { useState } from 'react'
const Controlled = () => {
  const [value, setValue] = useState(0)
  return (
    <RatingGroup.Root
      count={5}
      value={value}
      onValueChange={(details) => setValue(details.value)}
      allowHalf
    >
      <RatingGroup.Label>Label</RatingGroup.Label>
      <RatingGroup.Control>
        {({ items }) =>
          items.map((item) => (
            <RatingGroup.Item key={item} index={item}>
              {({ isHalf, isHighlighted }) => {
                if (isHalf) return <IconHalf />
                if (isHighlighted) return <IconFull />
                return <IconEmpty />
              }}
            </RatingGroup.Item>
          ))
        }
      </RatingGroup.Control>
    </RatingGroup.Root>
  )
}
import { RatingGroup } from '@ark-ui/solid'
import { Index, createSignal } from 'solid-js'
const Controlled = () => {
  const [value, setValue] = createSignal(0)
  return (
    <RatingGroup.Root
      count={5}
      value={value()}
      onValueChange={(details) => setValue(details.value)}
      allowHalf
    >
      <RatingGroup.Label>Label</RatingGroup.Label>
      <RatingGroup.Control>
        {(api) => (
          <Index each={api().items}>
            {(index) => (
              <RatingGroup.Item index={index()}>
                {(api) =>
                  api().isHalf ? <IconHalf /> : api().isHighlighted ? <IconFull /> : <IconEmpty />
                }
              </RatingGroup.Item>
            )}
          </Index>
        )}
      </RatingGroup.Control>
    </RatingGroup.Root>
  )
}
<script setup lang="ts">
import { ref } from 'vue'
import { RatingGroup } from '@ark-ui/vue'
import { IconEmpty, IconFull, IconHalf } from './rating-icons'
const value = ref(0)
</script>
<template>
  <RatingGroup.Root :count="5" v-model="value" allowHalf>
    <RatingGroup.Label>Label</RatingGroup.Label>
    <RatingGroup.Control v-slot="{ items }">
      <RatingGroup.Item
        v-for="item in items"
        :key="item"
        :index="item"
        v-slot="{ isHalf, isHighlighted }"
      >
        <IconHalf v-if="isHalf" />
        <IconFull v-else-if="isHighlighted" />
        <IconEmpty v-else />
      </RatingGroup.Item>
    </RatingGroup.Control>
  </RatingGroup.Root>
</template>
To make the rating group disabled, set the disabled prop to true.
import { RatingGroup } from '@ark-ui/react'
const Disabled = () => (
  <RatingGroup.Root count={5} defaultValue={3} disabled>
    <RatingGroup.Label>Label</RatingGroup.Label>
    <RatingGroup.Control>
      {({ items }) =>
        items.map((item) => (
          <RatingGroup.Item key={item} index={item}>
            {({ isHighlighted }) => {
              if (isHighlighted) return <IconFull />
              return <IconEmpty />
            }}
          </RatingGroup.Item>
        ))
      }
    </RatingGroup.Control>
  </RatingGroup.Root>
)
import { RatingGroup } from '@ark-ui/solid'
import { Index } from 'solid-js'
const Disabled = () => (
  <RatingGroup.Root count={5} value={3} disabled>
    <RatingGroup.Label>Label</RatingGroup.Label>
    <RatingGroup.Control>
      {(api) => (
        <Index each={api().items}>
          {(index) => (
            <RatingGroup.Item index={index()}>
              {(api) => (api().isHighlighted ? <IconFull /> : <IconEmpty />)}
            </RatingGroup.Item>
          )}
        </Index>
      )}
    </RatingGroup.Control>
  </RatingGroup.Root>
)
<script setup lang="ts">
import { ref } from 'vue'
import { RatingGroup } from '@ark-ui/vue'
import { IconEmpty, IconFull, IconHalf } from './rating-icons'
const value = ref(0)
</script>
<template>
  <RatingGroup.Root :count="5" :model-value="3" disabled>
    <RatingGroup.Label>Label</RatingGroup.Label>
    <RatingGroup.Control v-slot="{ items }">
      <RatingGroup.Item v-for="item in items" :key="item" :index="item" v-slot="{ isHighlighted }">
        <IconFull v-if="isHighlighted" />
        <IconEmpty v-else />
      </RatingGroup.Item>
    </RatingGroup.Control>
  </RatingGroup.Root>
</template>
To make the rating group readonly, set the readOnly prop to true.
import { RatingGroup } from '@ark-ui/react'
const ReadOnly = () => (
  <RatingGroup.Root count={5} defaultValue={3} readOnly>
    <RatingGroup.Label>Label</RatingGroup.Label>
    <RatingGroup.Control>
      {({ items }) =>
        items.map((item) => (
          <RatingGroup.Item key={item} index={item}>
            {({ isHighlighted }) => {
              if (isHighlighted) return <IconFull />
              return <IconEmpty />
            }}
          </RatingGroup.Item>
        ))
      }
    </RatingGroup.Control>
  </RatingGroup.Root>
)
import { RatingGroup } from '@ark-ui/solid'
import { Index } from 'solid-js'
const ReadOnly = () => (
  <RatingGroup.Root count={5} value={3} readOnly>
    <RatingGroup.Label>Label</RatingGroup.Label>
    <RatingGroup.Control>
      {(api) => (
        <Index each={api().items}>
          {(index) => (
            <RatingGroup.Item index={index()}>
              {(api) => (api().isHighlighted ? <IconFull /> : <IconEmpty />)}
            </RatingGroup.Item>
          )}
        </Index>
      )}
    </RatingGroup.Control>
  </RatingGroup.Root>
)
<script setup lang="ts">
import { ref } from 'vue'
import { RatingGroup } from '@ark-ui/vue'
import { IconEmpty, IconFull, IconHalf } from './rating-icons'
const value = ref(0)
</script>
<template>
  <RatingGroup.Root :count="5" :model-value="3" readOnly>
    <RatingGroup.Label>Label</RatingGroup.Label>
    <RatingGroup.Control v-slot="{ items }">
      <RatingGroup.Item v-for="item in items" :key="item" :index="item" v-slot="{ isHighlighted }">
        <IconFull v-if="isHighlighted" />
        <IconEmpty v-else />
      </RatingGroup.Item>
    </RatingGroup.Control>
  </RatingGroup.Root>
</template>
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.
import { RatingGroup } from '@ark-ui/react'
const FormUsage = () => (
  <RatingGroup.Root name="my-rating" count={5} defaultValue={3}>
    <RatingGroup.Label>Label</RatingGroup.Label>
    <RatingGroup.Control>
      {({ items }) =>
        items.map((item) => (
          <RatingGroup.Item key={item} index={item}>
            {({ isHighlighted }) => {
              if (isHighlighted) return <IconFull />
              return <IconEmpty />
            }}
          </RatingGroup.Item>
        ))
      }
    </RatingGroup.Control>
  </RatingGroup.Root>
)
import { RatingGroup } from '@ark-ui/solid'
import { Index } from 'solid-js'
const FormUsage = () => (
  <RatingGroup.Root name="my-rating" count={5} value={3}>
    <RatingGroup.Label>Label</RatingGroup.Label>
    <RatingGroup.Control>
      {(api) => (
        <Index each={api().items}>
          {(index) => (
            <RatingGroup.Item index={index()}>
              {(api) => (api().isHighlighted ? <IconFull /> : <IconEmpty />)}
            </RatingGroup.Item>
          )}
        </Index>
      )}
    </RatingGroup.Control>
  </RatingGroup.Root>
)
<script setup lang="ts">
import { ref } from 'vue'
import { RatingGroup } from '@ark-ui/vue'
import { IconEmpty, IconFull, IconHalf } from './rating-icons'
const value = ref(0)
</script>
<template>
  <RatingGroup.Root name="my-rating" :count="5" :model-value="3">
    <RatingGroup.Label>Label</RatingGroup.Label>
    <RatingGroup.Control v-slot="{ items }">
      <RatingGroup.Item v-for="item in items" :key="item" :index="item" v-slot="{ isHighlighted }">
        <IconFull v-if="isHighlighted" />
        <IconEmpty v-else />
      </RatingGroup.Item>
    </RatingGroup.Control>
  </RatingGroup.Root>
</template>
| Prop | Type | Default | 
|---|---|---|
| index | number | |
| asChildRender as a different element type. | boolean | 
| Prop | Type | Default | 
|---|---|---|
| allowHalfWhether to allow half stars. | boolean | |
| asChildRender as a different element type. | boolean | |
| autoFocusWhether to autofocus the rating. | boolean | |
| countThe total number of ratings. | number | |
| defaultValueThe initial value of the rating group. | number | |
| dirThe document's text/writing direction. | 'ltr' | 'rtl' | "ltr" | 
| disabledWhether the rating is disabled. | boolean | |
| formThe associate form of the underlying input element. | string | |
| getRootNodeA root node to correctly resolve document in custom environments. E.x.: Iframes, Electron. | () => Node | ShadowRoot | Document | |
| idThe unique identifier of the machine. | string | |
| idsThe ids of the elements in the rating. Useful for composition. | Partial<{
  root: string
  label: string
  hiddenInput: string
  control: string
  rating(id: string): string
}> | |
| nameThe name attribute of the rating element (used in forms). | string | |
| onHoverChangeFunction to be called when the rating value is hovered. | (details: HoverChangeDetails) => void | |
| onValueChangeFunction to be called when the rating value changes. | (details: ValueChangeDetails) => void | |
| readOnlyWhether the rating is readonly. | boolean | |
| translationsSpecifies the localized strings that identifies the accessibility elements and their states | IntlTranslations | |
| valueThe current rating value. | number | 
| Prop | Type | Default | 
|---|---|---|
| asChildRender as a different element type. | boolean | 
| Prop | Type | Default | 
|---|---|---|
| asChildRender as a different element type. | boolean | 
Previous
Range SliderNext
Segment Group