DocumentationComponentsTheme CreatorGitHub
Theme CreatorGitHubIntroduction
InstallationUsageTheming
ComponentsAccordionAction SheetAlertAlertDialogArea ChartAspectRatioAvatarBadgeBannerBar ChartBottom NavBreadcrumbButtonButtonGroupCalendarCardCarouselChartChatBubbleChatBubbleNewCheckboxChipCoachMarkCodeBlockCollapsibleColor PickerComboboxCommandContainerContextMenuDate PickerDividerDrawerDropdown MenuEmptyStateFABFieldFieldsetFile UploadFlexFormGridHoverCardIconButtonLabelLine ChartLinkMenubarMeterModalNavigation MenuNumberFieldOTP InputPaginationPickerPie ChartPopoverProgressPromptInputRadar ChartRadial ChartRadioRatingScroll AreaSearchSegmentedControlSelectShortcutSidebarSkeletonSliderCircularSliderMediaTrimmerSpacerSpinnerSplit ButtonStackStatusStepperSurfaceSwitchTableTabsTextareaTime PickerToastToggleButtonToggleGroupTokenizerToolbarTooltipTop Header DesktopTop Header Mobile
Contributing
Components

Rating

Captures a score in four shapes — star scale, numeric NPS scale, emoji reaction picker and binary thumbs — as a keyboard-operable form control, or as a read-only display that paints an exact fractional average.

Playground

Installation

pnpm add @tessinaui/ui

Usage

import { Rating } from "@tessinaui/ui";
{/* Star (default) */}
<Rating label="Overall rating" defaultValue={4} />

{/* Half-star */}
<Rating label="Overall rating" precision={0.5} defaultValue={3.5} />

{/* NPS numeric scale — set min={0}, since NPS runs 0–10 */}
<Rating
  variant="numeric"
  label="Likelihood to recommend"
  min={0}
  max={10}
  lowLabel="Not likely"
  highLabel="Extremely likely"
/>

{/* Emoji reactions */}
<Rating variant="reaction" label="How do you feel?" />

{/* Binary thumbs */}
<Rating variant="binary" label="Was this helpful?" />

{/* Read-only display of an aggregate score */}
<Rating readOnly value={4.6} />

When to use which

Rating is a form control: it holds a value, it can be submitted, and it is operable from the keyboard. Three neighbouring patterns look similar and are not this component.

You wantUse
A score the person picks and you submitRating
A score you are only displayingRating with readOnly — it becomes a labelled image, not a set of unusable radios
Thumbs in a chat or AI message toolbarIconButton — those act immediately, hold no value, and belong in the message's action row
A distribution of ratings (5★ 82%, 4★ 11%…)Meter per row — see the summary example
A bipolar attribute scale ("runs small … runs large")Slider
A single yes/no answer with no scaleCheckbox or Radio

Examples

Default

The star scale, uncontrolled. label names the group for assistive technology — always pass it, or a screen reader announces only "Star rating".

Variants

Four shapes on one control. star for review scores, numeric for NPS and CSAT scales, reaction for quick sentiment, binary for a helpful/not-helpful answer. They share every prop; only the anatomy differs.

Sizes

Five steps from xs to xl. The boxes run the input family's ladder, so a md Rating lines up with a md Field in the same form row. On touch viewports every target floors at 44px regardless of the size you pick.

Intents

intent carries meaning, not decoration. Star keeps warning — amber stars are the universal convention — and every other shape defaults to the neutral none, which is what most real scales render.

Half-star precision

precision={0.5} lets someone pick a half. The half is real, not cosmetic: arrows step by 0.5 and the selected star announces "3.5 stars", so the value a screen-reader user hears is the value you receive.

A word for the value

showValue renders the readout; getValueLabel decides what it says. It is one seam for both the visible text and the accessible value, so a descriptor like "Very good" cannot drift from what assistive technology announces.

Anchor labels

lowLabel and highLabel pin the ends of the scale, which is what stops a 0–10 row being ambiguous about which end is good. They take any node, so an emoji pair works as well as words.

Labels under each item

itemLabelPlacement="below" captions every step. Use it when the steps are not self-evident — a five-point quality scale reads far better as Poor · Fair · Good · Very good · Exceptional than as five identical stars.

NPS scale

Net Promoter Score runs 0–10, so pass min={0}: an eleven-box scale, not a ten-box one. The row wraps rather than overflowing on a narrow screen.

Clearing the value

allowClear lets someone undo a rating — select the current value again, or press Delete. Without it a mis-tap is permanent, which is why the callback reports reason: "clear" separately from a normal selection.

Custom icons

icon and emptyIcon replace the star glyph. Keep both the same shape: the filled/empty distinction is carried by solid-vs-outline, which is what makes the scale readable without relying on colour alone.

Read-only display

A score you are showing is not a disabled input. readOnly renders one labelled image announcing "4.6 of 5", and the stars paint the exact fraction — 4.6 fills 60% of the fifth star rather than rounding to a half. Pair max={1} with your own number for the compact single-star form.

Summary with a breakdown

The average, the count and the per-star distribution are a composition, not props: a read-only Rating for the average and a Meter per row for the bars. The histogram is Meter's job, so it stays themeable and accessible on its own.

Inside a form

name renders a hidden input, so the value submits with the rest of the form; required and errorMessage wire the invalid state to the group. Multiple labelled rows are the standard shape for a multi-category review.

Rating, then follow-up

Asking for detail only after the score arrives is the pattern nearly every delivery and marketplace app uses — the first tap is cheap, and the follow-up is optional.

Binary as a form control

The thumbs pair is a real radiogroup with a name and a value. That is what separates it from the icon-only thumbs in a chat toolbar, which act immediately and belong to IconButton.

On a coloured surface

tone="on-color" is inherited from a Surface, so a rating inside a coloured panel derives every ink from that panel's own text colour. It needs no cooperation from the plate and is correct on a tint, a dark panel or a photo.

Right to left

dir="rtl" mirrors the row and the keyboard: ← moves forward, matching the visual order. Pass a translated getValueLabel so the readout is localised too.

Controlled

Pass value with onValueChange to own the state. The second argument reports why the value changed — pointer, keyboard or clear — which is what you need to tell a deliberate reset from a fresh selection.

States

Read-only shows the value, disabled takes the control out of the form, and errorMessage marks the group invalid and describes it.

Skeleton

skeleton renders the placeholder from the component's own geometry, so the boxes, the radius and the mobile touch floor match what loads in — including the eleven boxes of a 0–10 scale.

Variants

Variant options

VariantPatternUse case
star★★★★☆Product ratings, review scores
numeric1 2 3 … 10NPS, customer satisfaction (CSAT)
reaction😡 😕 😐 🙂 😄App feedback, quick sentiment
binary👎 👍Helpful / not helpful

Intent options

none (default for numeric, reaction and binary), warning (default for star), error, success, info. primary is a deprecated alias for none.

Size options

xs, sm, md (default), lg, xl — the input family's ladder.

Rounded options

none, sm, md, lg, full. Defaults per variant: star sm, numeric md, reaction and binary full.

API Reference

Rating Props

PropTypeDefaultDescription
variant"star" | "numeric" | "reaction" | "binary""star"Visual pattern
valuenumber | null—Controlled value (null = nothing selected)
defaultValuenumber | nullnullUncontrolled initial value
onValueChange(value, { reason }) => void—Called when the value changes. reason is "pointer" | "keyboard" | "clear"
maxnumber5 (star, reaction, binary) / 10 (numeric)Highest value
min0 | 11Numeric only — lowest value. NPS runs from 0
size"xs" | "sm" | "md" | "lg" | "xl""md"Density
intent"none" | "warning" | "error" | "success" | "info""warning" (star) / "none" (rest)Semantic colour
tone"default" | "on-color"inherited from SurfaceSet on a coloured plate
readOnlybooleanfalseDisplay mode — role="img", exact fractional fill, no input
disabledbooleanfalseForm-disabled; the disabled tokens on the page, the plate's own ink dimmed on a coloured surface
precision1 | 0.51Star only — 0.5 enables half-star selection
allowClearbooleanfalseRe-selecting the current value (or Delete) clears it
dir"ltr" | "rtl""ltr"Mirrors the row and the horizontal arrow keys
icon / emptyIconReactNode—Star only — custom filled / empty glyph
itemsRatingItem[]built-inReaction and binary — the option list
itemLabelPlacement"inline" | "below" | "hidden""inline" (reaction, binary) / "hidden" (star, numeric)Where each item's label sits. Hidden labels stay in the accessibility tree
showValuebooleanfalseRender the value readout
valuePosition"end" | "below""below"Where the readout sits
getValueLabel(value, max) => string"3 of 5" / "Not rated"The readout text and the read-only accessible name
getItemLabel(value, max) => string"3 stars" / "3 of 5"One item's accessible name
lowLabel / highLabelReactNode—Labels pinned to the ends of the scale
labelstringvariant defaultThe group's accessible name
namestring—Submit the value under this name (renders a hidden input)
requiredbooleanfalseMarks the group required for the form and assistive tech
errorMessageReactNode—Error text below the control; implies an invalid state
rounded"none" | "sm" | "md" | "lg" | "full"per variantCorner radius of each item
skeletonbooleanfalseRender the loading placeholder instead
classNamestring—Additional classes on the root

Deprecated for one minor, still working: onChange (use onValueChange), showLabel (use showValue for the readout and label for the name), and intent="primary" (use intent="none"). Each warns once in development.

RatingItem

Used by items for the reaction and binary variants.

FieldTypeDescription
valuenumberUnique option value
labelstringThe option's accessible name, and its visible label unless hidden
iconReactNodeOptional glyph rendered before the label

RatingSkeleton Props

variant, size, rounded, max, min, items, dir — the geometry props, so the placeholder is the same box as the component. Plus label: the skeleton is silent by default and only announces when you give it one, because a loading region should announce once, not once per placeholder.

Accessibility

  • Roles. The interactive control is a radiogroup of radios. Exactly one radio is ever checked — including at half precision, where the checked star announces the real value ("3.5 stars"). readOnly is not a disabled radiogroup: it renders a single role="img" labelled with the value, so a score is announced once instead of presenting options nobody can use.
  • Keyboard. The group is one Tab stop on every variant. ← → ↑ ↓ move the value (by precision on a star scale), Home and End jump to the ends, and Delete / Backspace clear when allowClear is set. Under dir="rtl" the horizontal arrows mirror, so ← moves forward.
  • Naming. Pass label. getValueLabel and getItemLabel are the localisation seams for the value and the individual options; items carry their own labels.
  • Forms. required sets aria-required, errorMessage sets aria-invalid and describes the group, and name submits the value through a hidden input.
  • Colour is never the only signal. Filled and empty are different glyphs (solid vs outline), not two tints of one colour, and the empty glyph is drawn in the boundary token gated at 3:1.
  • Touch. Every target floors at 44×44 on touch viewports (WCAG 2.5.5) while desktop keeps the compact density.
  • Tested with VoiceOver on macOS. NVDA and JAWS are untested.
RadioScroll Area

On this page

PlaygroundInstallationUsageWhen to use whichExamplesDefaultVariantsSizesIntentsHalf-star precisionA word for the valueAnchor labelsLabels under each itemNPS scaleClearing the valueCustom iconsRead-only displaySummary with a breakdownInside a formRating, then follow-upBinary as a form controlOn a coloured surfaceRight to leftControlledStatesSkeletonVariantsVariant optionsIntent optionsSize optionsRounded optionsAPI ReferenceRating PropsRatingItemRatingSkeleton PropsAccessibility