Star ratings sound simple until you ship them to production. Then you need half-star precision, accessible keyboard navigation, RTL layouts, flexible icon sources, and correct ARIA semantics. I built vue-star-rate to handle all of that in a single zero-dependency Vue 3.5+ component.
Installation
pnpm add vue-js-star-ratingRequires Vue 3.5+. Uses defineModel and useTemplateRef — both stable in Vue 3.5. Zero runtime dependencies.
Basic Usage
<script setup lang="ts">
import { ref } from 'vue';
import { VueStarRate } from 'vue-js-star-rating';
import 'vue-js-star-rating/dist/style.css';
const rating = ref(0);
</script>
<template>
<VueStarRate v-model="rating" />
</template>Half-Star Ratings
<VueStarRate v-model="rating" :allow-half="true" :show-counter="true" />The visual renderer fills exactly half of a star glyph. The emitted value is a decimal like 3.5.
Size Presets
<VueStarRate v-model="rating" size="xs" /> <!-- 16px -->
<VueStarRate v-model="rating" size="sm" /> <!-- 20px -->
<VueStarRate v-model="rating" size="md" /> <!-- 24px — default -->
<VueStarRate v-model="rating" size="lg" /> <!-- 32px -->
<VueStarRate v-model="rating" size="xl" /> <!-- 40px -->
<VueStarRate v-model="rating" :icon-size="28" /> <!-- Custom pixels -->Custom Colors
<VueStarRate
v-model="rating"
:colors="{
empty: '#27272a',
filled: '#fbbf24',
hover: '#fcd34d',
half: '#fbbf24',
}"
/>Icon Providers
<!-- Lucide (requires lucide-vue-next) -->
<VueStarRate v-model="rating" icon-provider="lucide" />
<!-- FontAwesome (requires @fortawesome/fontawesome-free) -->
<VueStarRate v-model="rating" icon-provider="fontawesome" />
<!-- Fully custom SVG via slot -->
<VueStarRate v-model="rating" icon-provider="custom">
<template #icon="{ filled, size, color }">
<svg :width="size.width" :height="size.height" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="10"
:fill="filled ? color : 'none'"
:stroke="color" stroke-width="2" />
</svg>
</template>
</VueStarRate>Read-Only Mode
For review cards, dashboards, and product pages:
<VueStarRate :model-value="4.5" :readonly="true" :allow-half="true" :show-counter="true" />Keyboard Navigation
| Key | Action |
|---|---|
| Arrow Right / Up | Increase rating |
| Arrow Left / Down | Decrease rating |
| Home | Set to minimum |
| End | Set to maximum |
| 1–9 | Jump to specific value |
| 0 | Reset to minimum |
The component uses role="group", aria-pressed on each star, and an aria-live counter — fully WCAG 2.2 compliant.
Tooltips and Counters
<VueStarRate v-model="rating" :show-counter="true" counter-template="{value} / {max}" />
<VueStarRate
v-model="rating"
:show-tooltip="true"
:tooltip-labels="['Terrible', 'Poor', 'Fair', 'Good', 'Excellent']"
/>Full Configuration Example
<VueStarRate
v-model="rating"
:max-stars="5"
:allow-half="true"
:show-counter="true"
:show-tooltip="true"
size="lg"
:colors="{ empty: '#27272a', filled: '#fbbf24', hover: '#fcd34d', half: '#fbbf24' }"
:animation="{ enabled: true, duration: 200, type: 'scale' }"
:clearable="true"
@change="(val, old) => console.log(val, old)"
/>Props Reference
| Prop | Type | Default | Description |
|---|---|---|---|
v-model | number | 0 | Rating value |
maxStars | number | 5 | Maximum stars |
allowHalf | boolean | false | Half-star precision |
size | xs / sm / md / lg / xl | md | Size preset |
readonly | boolean | false | Display-only mode |
clearable | boolean | false | Clear button |
showCounter | boolean | false | Numeric counter |
showTooltip | boolean | false | Hover tooltips |
rtl | boolean | false | Right-to-left layout |
iconProvider | custom / lucide / fontawesome | custom | Icon source |
Programmatic Control
const ratingRef = ref<InstanceType<typeof VueStarRate>>();
ratingRef.value?.reset();
ratingRef.value?.setRating(3.5);
ratingRef.value?.getRating();
ratingRef.value?.focus();Migration from v2
| v2 | v3 |
|---|---|
lucideIcons prop | icon-provider="lucide" |
role="slider" | role="group" (WCAG 2.2) |
animation: { scale: 1.15 } | animation: { type: 'scale' } |
Vue ^3.3.0 peer dep | Vue ^3.5.0 peer dep |
