Introduction
Any icon, any precision, accessible. A headless, zero-dependency React rating component. It
renders a score as a row of icons with exact partial fills — and becomes a fully accessible input
the moment you add onChange.
Most React “star rating” widgets make three assumptions that fall apart in production:
- Stars are hardcoded. You get a star, maybe a color, maybe a size. The moment design asks for hearts, an emoji, or your own SVG, you are forking the library.
- Precision is a boolean.
half={true}is the ceiling. A backend that returns4.28has nowhere to go — it is either rounded away or it throws. - Accessibility is absent. Bare
<div>s withonClick. No role, no keyboard, invisible to a screen reader.
@rxova/react-rating-input treats a rating as a primitive, not a widget. It owns exactly two things —
geometry and semantics — and hands appearance entirely to you.
Exact fractional geometry
Section titled “Exact fractional geometry”A score like 4.3 is drawn as four full icons and a fifth icon clipped to 30% width with a CSS
overflow layer. The fill is an exact measurement, not one of a few baked-in states, so any value in
[0, max] renders precisely. Because the fill is a width clip over whatever icon you pass, it works
identically for SVG, emoji, an <img>, or arbitrary JSX.
Rounding is a separate, explicit decision (see Displaying a score): two
orthogonal props, precision (the grid) and rounding (the direction). Snapping is display-only
and never writes a rounded value back into your state.
Accessibility by construction
Section titled “Accessibility by construction”| Mode | Semantics |
|---|---|
| Read-only | role="img" with an aria-label; icons hidden; not a tab stop |
| Interactive | role="radiogroup" of native radios; one tab stop; arrow-key nav |
Providing onChange is the only thing that flips read-only into interactive. Because interactive
mode renders native, visually-hidden radio inputs, keyboard navigation, focus-visible, form
participation, and screen-reader announcements come from the browser — not from JavaScript that has
to reimplement them (imperfectly). Details in Accessibility.
Small, honest packaging
Section titled “Small, honest packaging”- Zero runtime dependencies.
reactis the only peer. - No stylesheet. Only layout-critical CSS is inlined; everything visual is a
--rfs-*custom property or adata-*hook, both covered by semver. - ~2.5 kB brotli for the full component;
useRatingis ~900 B on its own for a fully custom renderer. - Dual ESM/CJS with correct types in both, verified in CI by
publintand@arethetypeswrong/cli, plus a pack-and-import smoke test.
The result is a component you can adopt in a design system without inheriting a design, and drop into a production form without auditing it for accessibility first.