StarRating

View source
An interactive star rating component.

Introduction

MStarRating is a feature-rich star rating component. It supports full and half-star ratings, readonly display, custom total stars and icons, keyboard interaction, badge display, and highlighted focus state.

Built on Nuxt UI's Button and Badge components

Usage

Default 5-star rating system. Click a star to write to v-model:

3/5
<script setup lang="ts">
const value = ref(3)
</script>

<template>
  <MStarRating v-model="value" />
</template>

allowHalf Half-Star Rating

allowHalf allows each star to take a half value. Clicking the left or right half records .5 or a whole number rating respectively:

3.5/5
<script setup lang="ts">
const value = ref(3.5)
</script>

<template>
  <MStarRating v-model="value" allow-half />
</template>

clearable Clearable

clearable allows clicking the current value again or pressing Backspace to reset the rating to zero — useful for optional fields:

2.5/5
<script setup lang="ts">
const value = ref(2.5)
</script>

<template>
  <MStarRating v-model="value" allow-half clearable />
</template>

readonly Readonly Mode

readonly displays the rating visually without allowing interaction — used for review and result display:

4/5
<script setup lang="ts">
const value = ref(4)
</script>

<template>
  <MStarRating v-model="value" readonly />
</template>

max Total Stars

max adjusts the number of stars to support finer-grained rating scales:

5/7
<script setup lang="ts">
const value = ref(5)
</script>

<template>
  <MStarRating v-model="value" :max="7" />
</template>

emptyIcon Rating Icons

emptyIcon, filledIcon, and halfIcon can replace the default icons entirely. Use with allowHalf to render half values:

2.5/5
<script setup lang="ts">
const value = ref(2.5)
</script>

<template>
  <MStarRating
    v-model="value"
    allow-half
    empty-icon="i-lucide-heart"
    filled-icon="i-lucide-heart"
    half-icon="i-lucide-heart-handshake"
  />
</template>

showBadge Rating Badge

showBadge shows the current score badge by default. Set to false to show only the stars:

<script setup lang="ts">
const value = ref(3)
</script>

<template>
  <MStarRating v-model="value" :show-badge="false" />
</template>

color Color

color sets the color of selected stars and the badge:

4/5
<script setup lang="ts">
const value = ref(4)
</script>

<template>
  <MStarRating v-model="value" color="primary" />
</template>

highlight Highlight Focus

highlight adds a ring-style highlight to the rating control to emphasize the currently interactive item:

3/5
<script setup lang="ts">
const value = ref(3)
</script>

<template>
  <MStarRating v-model="value" highlight />
</template>

buttonProps Button Properties

buttonProps is passed to each star's button, allowing uniform adjustment of variant, padding, and other underlying styles:

3/5
<script setup lang="ts">
const value = ref(3)
</script>

<template>
  <MStarRating v-model="value" :button-props="{ variant: 'soft', size: 'xs' }" />
</template>

disabled Disabled State

disabled prevents interaction and reduces opacity simultaneously:

4/5
<script setup lang="ts">
const value = ref(4)
</script>

<template>
  <MStarRating v-model="value" disabled />
</template>

Examples

Inheriting Field Context

When placed inside UFormField, it receives field size and error state, and rating icons update with the form state:

Example error state
<template>
  <UFormField label="Satisfaction" size="xs" error="Example error state">
    <MStarRating />
  </UFormField>
</template>

Inside UFieldGroup

When placed alongside a reset button inside UFieldGroup, they share size — suitable for combining actions in compact form rows:

<template>
  <UFieldGroup size="xs">
    <MStarRating />
    <UButton icon="i-lucide-rotate-ccw" color="neutral" variant="subtle" />
  </UFieldGroup>
</template>

Event Callbacks

Click, hover, and keyboard interactions fire update:modelValue, change, and hover in sequence:

当前评分: 0
悬停星级: -
<script setup lang="ts">
const toast = useToast()
const rating = ref(0)
const hovering = ref<number | null>(null)

function handleChange(value: number) {
  toast.add({
    title: '评分已更改',
    color: 'success',
    description: `您选择的评分是 ${value}`
  })
}

function handleHover(value: number | null) {
  hovering.value = value
}
</script>

<template>
  <div class="space-y-2">
    <MStarRating v-model="rating" @change="handleChange" @hover="handleHover" />

    <div class="text-sm text-gray-500">
      <div>当前评分: {{ rating }}</div>
      <div>悬停星级: {{ hovering ?? '-' }}</div>
    </div>
  </div>
</template>

Custom Slots

Add prefix, suffix, or a custom badge via slots:

评分:
4/5 分 (128 评价)
<script setup lang="ts">
const rating = ref(4)
</script>

<template>
  <MStarRating v-model="rating">
    <template #prefix>
      <span class="text-sm text-gray-500 mr-2">评分:</span>
    </template>

    <template #badge="{ label }">
      <span class="text-sm text-primary font-semibold ml-2"> {{ label }} 分 </span>
    </template>

    <template #suffix>
      <span class="text-xs text-gray-400 ml-2">(128 评价)</span>
    </template>
  </MStarRating>
</template>

Keyboard Navigation

The component supports full keyboard interaction for improved accessibility:

  • Arrow keys: increment/decrement the rating (supports half-star stepping)
  • Shortcuts: Home sets the minimum rating, End sets the maximum rating
  • Number keys: 0-9 jump directly to the corresponding rating
  • Clear keys: Backspace / Delete clear the rating (requires clearable to be enabled)
The component follows WCAG accessibility guidelines, including complete ARIA attributes and keyboard focus management

API

Props

Prop Default Type
max5number

最大星级数

emptyIcon'i-lucide-star'any

未选中星星的图标

filledIcon'i-lucide-star'any

选中星星的图标

halfIcon'i-lucide-star-half'any

半星图标

idstring
namestring
buttonPropsButtonProps
color"primary" | "secondary" | "info" | "success" | "warning" | "error" | "important" | "neutral"

选中星星的颜色

size'md'"xs" | "sm" | "md" | "lg" | "xl"

星星大小

modelValue0number
showBadgetrueboolean

是否显示评分徽章

disabledboolean

是否禁用

highlightboolean

Highlight the ring color like a focus state.

readonlyboolean

是否只读

allowHalfboolean

是否允许半星

clearableboolean

是否允许清除评分

uiRecord<string, ClassNameValue> & { root?: SlotClass; stars?: SlotClass; star?: SlotClass; }

Emits

Event Type
update:modelValue[value: number]
change[value: number]
hover[value: number]

Slots

Slot Type
prefix{ value: number; max: number; }
badge{ value: number; max: number; label: string; }
suffix{ value: number; max: number; }

Theme

app.config.ts
export default defineAppConfig({
  ui: {
    starRating: {
      slots: {
        root: 'inline-flex items-center gap-1',
        stars: 'flex items-center gap-0.5',
        star: 'transition-all duration-150'
      },
      variants: {
        interactive: {
          true: {
            star: 'cursor-pointer hover:scale-110'
          }
        },
        disabled: {
          true: {
            star: 'cursor-not-allowed opacity-50'
          }
        },
        readonly: {
          true: {
            star: 'cursor-default'
          }
        },
        fieldGroup: {
          horizontal: {
            root: 'rounded-md bg-default ring ring-inset ring-accented px-1 not-only:first:rounded-e-none not-only:last:rounded-s-none not-last:not-first:rounded-none focus-visible:z-[1]'
          },
          vertical: {
            root: 'rounded-md bg-default ring ring-inset ring-accented px-1 not-only:first:rounded-b-none not-only:last:rounded-t-none not-last:not-first:rounded-none focus-visible:z-[1]'
          }
        }
      }
    }
  }
})

Changelog

No recent changes
Copyright © 2025 - 2026 YiXuan - MIT License