Rating

Free · MITReact Native · Expo

Stars (or any symbol) you tap or drag across: the fill sweeps from the old rating to the new one and every symbol it crosses pops on its own beat; half steps optional, and a read-only variant shows exact fractions with a value and a count.

View as Markdown
Try it on your phone
Scan to open this demo in your phone's browser and use it with your fingers.

Installation

$ npx shadcn@latest add https://goodui.dev/native/r/rating.json

Then run npx expo install --fix so react-native-gesture-handler, react-native-reanimated, react-native-svg match your Expo SDK. Files land in components/goodui-native/rating.

Usage

import { useState } from "react";
import { Rating } from "@/components/goodui-native/rating/rating";

export function RateStay() {
  const [stars, setStars] = useState(0);
  return (
    <Rating
      size="lg"
      value={stars}
      onValueChange={setStars}
      onValueCommit={(v) => saveReview({ stars: v })}
      labels={["Terrible", "Bad", "Okay", "Good", "Great"]}
      accessibilityLabel="Your stay"
    />
  );
}

When to use

Use it for
  • Asking for a review: a stay, a ride, an order, a support conversation.
  • Showing an average score with its number of ratings in lists and product pages (readOnly with showValue and count).
  • A small ordinal scale with a meaning per step, such as effort or difficulty, with labels.
Reach for something else
  • A continuous value or more than ten steps: use slider.
  • Two or three named choices: use segmented-control or chip.
  • A like or favorite: use like-button.

Examples

Half steps

<Rating allowHalf defaultValue={3.5} accessibilityLabel="Wine rating" />

Read-only score in a list row

<Rating readOnly value={4.6} count={1284} showValue size="sm" />

Custom symbol

<Rating
  value={effort}
  onValueChange={setEffort}
  allowClear
  icon={({ filled, color, size }) => <FlameIcon color={color} size={size} filled={filled} />}
/>

API reference

PropTypeDefault
value
The rating (controlled). Pair it with `onValueChange`. 0 means no rating yet.
number—
defaultValue
Initial rating when uncontrolled. Default 0.
number0
onValueChange
Called each time the rating changes, also while the finger slides across.
(value: number) => void—
onValueCommit
Called once when a tap or a drag ends, with the final rating. Save it here.
(value: number) => void—
max
Number of symbols. Default 5.
number5
allowHalf
Half steps (3.5). Read-only ratings always show the exact fraction.
booleanfalse
allowClear
Tapping the current rating clears it back to 0. Default `false`.
booleanfalse
readOnly
Display only: shows fractional values exactly (4.3), with optional `showValue` and `count`.
booleanfalse
count
Read-only: the number of ratings, shown as "(1,284)".
number—
showValue
Read-only: the numeric value before the symbols, one decimal.
booleanfalse
labels
One word per value, for example `["Terrible", "Bad", "Okay", "Good", "Great"]`. Shown beside the symbols and read as the value.
string[]—
size
Symbol size: 16 (`sm`, for lists), 32 (`md`) or 44 pt (`lg`, for a review screen).
"sm" | "md" | "lg""md"
tone
Fill color: the accent, or the foreground for a quieter rating. Use an amber ThemeProvider accent for gold stars.
"accent" | "neutral""accent"
icon
A different symbol (heart, thumb, flame). Defaults to a star.
RatingIconStarIcon
disabled
Dims the control and ignores presses.
booleanfalse
haptic
`false` silences the tick per step.
booleantrue
accessibilityLabel
Screen reader name. Default "Rating" (the pack's `group`).
string—
style
Style for the outer container.
StyleProp<ViewStyle>—
testID
Identifier for end-to-end tests.
string—

Motion and haptics

  • A tap sweeps the fill from the old rating to the new one in 90 to 420 ms depending on the distance; each symbol pops (a swell, a small tilt, then the bouncy spring) the moment the fill crosses one of its half steps, so 1 to 5 ripples left to right. Lowering the rating dips the emptied symbols instead.
  • While dragging, the fill follows the finger step by step on a 120 ms timing, with the same pops.
  • The label crossfades when the value changes.
  • Reduced motion: the fill jumps to the new value, symbols do not pop, labels change without fading.
  • Haptic: select each time the value changes, by tap, drag, keyboard or screen reader (haptic={false} turns it off)
  • The fill and pops run on the UI thread: one shared level drives every symbol's clip width, and each symbol reacts to it in a worklet. React only re-renders when the snapped value changes.

Accessibility

  • The input is one adjustable element (a slider on the web) with aria-valuemin 0, aria-valuemax, aria-valuenow and a value text such as '4 of 5, Good' or 'Not rated'.
  • Screen readers step it with increment and decrement; on the web the arrow keys step it (mirrored in right-to-left layouts), Home and End jump to the ends.
  • The touch area is 44 pt tall; dragging anywhere across the row sets the value, so small symbols stay easy to hit.
  • Read-only ratings are one image labelled with the visible number first: '4.6 out of 5 stars, 1,284 ratings'.
  • Labels beside the symbols are hidden from screen readers because the same word is in the value text.

Notes for AI

  • Controlled: value + onValueChange (fires during a drag too). Save or send in onValueCommit, which fires once per tap or drag.
  • 0 means not rated. allowClear lets a tap on the current rating return to 0; otherwise the lowest value is one step.
  • allowHalf gives 0.5 steps for input. readOnly always shows the exact fraction of value (4.3 fills 30 % of the fifth star).
  • tone is 'accent' or 'neutral'. For gold stars set an amber accent on ThemeProvider; do not pass hex colors.
  • icon receives { filled, color, size } and must draw the filled and empty look; the component clips it for halves.
  • Sizes are sm 16 (lists, display), md 32 and lg 44 (review screens).