npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-awesome-stars-rating

v1.4.2

Published

Accessible star rating component for React, with half stars, keyboard control and any scale or palette.

Readme

React Awesome Stars Rating

npm CI license

An accessible star rating component for React. Half stars from pointer position, full keyboard control, any scale or palette — in approximately 3 kB gzipped with no runtime dependencies.

Live demo · Storybook

react-awesome-stars-rating preview

Highlights

  • Approximately 3 kB gzipped (ESM under 3 kB), no runtime dependencies
  • Half-star precision from pointer position, plus arrow-key stepping
  • role="slider" with live aria-valuenow / aria-valuetext, verified with axe-core
  • ESM + CJS with correct types for each, and a UMD bundle for CDN use
  • Ships 'use client', so it drops into a React Server Component tree unwrapped
  • Works with React Hook Form, Formik and TanStack Form

Requirements

React 18 or React 19 (react and react-dom are peer dependencies). The published browser package has no Node runtime requirement. Installed-consumer CI verifies React 18 and 19 across Node 20, 22 and 24, including server rendering and hydration.

Upgrading from 0.x? See the migration guide.

Installation

npm install react-awesome-stars-rating

CDN

React 19 no longer publishes UMD builds, so load the package as a module:

<script type="module">
  import React from 'https://esm.sh/react@19';
  import { createRoot } from 'https://esm.sh/react-dom@19/client';
  import ReactStarsRating from 'https://esm.sh/react-awesome-stars-rating';

  createRoot(document.getElementById('root')).render(
    React.createElement(ReactStarsRating, { value: 3.5, isEdit: false }),
  );
</script>

A UMD bundle is still published at dist/index.umd.cjs for pages that already expose React as a global, and expects window.React to be present.

Usage

import { useState } from 'react';
import ReactStarsRating from 'react-awesome-stars-rating';

export default function Example() {
  const [rating, setRating] = useState(3.5);
  return <ReactStarsRating value={rating} onChange={setRating} />;
}

Use value with a state-updating onChange when your application owns the rating. For a self-managed input, use <ReactStarsRating defaultValue={3.5} />. Do not switch between controlled and uncontrolled usage during an input's lifetime.

Clear a rating

Keep zero as the empty rating and provide an explicitly labeled button:

const [rating, setRating] = useState(0);

<>
  <ReactStarsRating
    value={rating}
    onChange={setRating}
    ariaLabel="Your rating"
  />
  <button type="button" onClick={() => setRating(0)}>
    Clear rating
  </button>
</>;

Display an average

Use readOnly for aggregate scores, with visible text so the number and review count are available without interpreting the star graphic:

<>
  <ReactStarsRating value={4.3} readOnly ariaLabel="Average rating" />
  <span>4.3 out of 5 · 128 reviews</span>
</>

With isHalf, the SVG displays partial fill for fractional values; editing still selects half-star steps. Do not round an average just to display it.

Next.js App Router

The published entry point includes "use client". A Server Component can render a read-only rating with serializable props. Put state and callbacks inside your own Client Component ("use client" at the top of its file), as in the usage example above; do not pass ordinary callback functions from a Server Component.

Props

| Name | Description | Type | Default | | :--------------- | :---------------------------------------- | :----------------------------------------- | :-------------- | | id | Identifier | string | auto-generated | | value | Controlled current value | number | undefined | | defaultValue | Initial uncontrolled value | number | 0 | | onChange | Called when value changes | (value: number) => void | () => {} | | onBlur / onFocus | Native slider focus callbacks | React.FocusEventHandler<HTMLSpanElement> | undefined | | dir | Layout, fill and horizontal key direction | 'ltr' \| 'rtl' | 'ltr' | | name | Hidden native form input name | string | undefined | | form | Associated form ID | string | undefined | | disabled | Disable interaction and form submission | boolean | false | | readOnly | Prevent editing | boolean | false | | isEdit | Deprecated inverse editing switch | boolean | true | | isHalf | Allow half stars | boolean | true | | count | Number of stars | number | 5 | | size | Star size (px) | number | 25 | | starGap | Gap between stars | number | 0 | | className | Container class | string | '' | | style | Container style and CSS variables | ReactStarsRatingStyle | undefined | | primaryColor | Active star color | string | 'orange' | | secondaryColor | Inactive star color | string | 'grey' | | hoverColor | Optional pointer-preview color | string | undefined | | isArrowSubmit | Arrow keys trigger onChange immediately | boolean | false | | ariaLabel | Accessible label (if no ariaLabelledBy) | string | 'Star rating' | | ariaLabelledBy | ID of element that labels the control | string | undefined | | getValueText | Formats accessible value text | (value, count) => string | English text |

Keyboard and accessibility

The control renders as a single role="slider" element with aria-valuemin, aria-valuemax, aria-valuenow and a readable aria-valuetext ("3.5 of 5"). Values outside 0…count are clamped before they are reported.

| Key | Behaviour | | :-------------- | :------------------------------------------------------------ | | / | Decrease by half a star (isHalf) or a whole star | | / | Increase by half a star (isHalf) or a whole star | | Home / End | Move to the minimum (0) or maximum (count) | | Enter | Report the current value through onChange | | Tab away/back | Report an unsubmitted keyboard change and remain re-focusable |

With isArrowSubmit, each arrow step reports through onChange immediately rather than waiting for Enter or blur.

Pointer changes commit only when a primary, left-button gesture starts on the control and completes. Right/middle clicks, unrelated releases and canceled gestures do not submit a rating. Touch dragging uses the same gesture tracking.

onFocus and onBlur receive native React focus events without replacing the internal focus handling. A pending keyboard change is committed before onBlur, so form libraries can mark the field touched with its latest value.

When readOnly is true the control is inert regardless of isArrowSubmit: no hover preview, no keyboard, tabIndex={-1}, and aria-readonly="true". The older isEdit={false} API remains supported but is deprecated.

The slider explicitly reports aria-orientation="horizontal". Use getValueText to localize its spoken value:

<ReactStarsRating
  value={2.5}
  getValueText={(value, count) => `${value} von ${count} Sternen`}
/>

Right-to-left layouts

Pass dir="rtl" explicitly to reverse star order, half-star filling, pointer selection and horizontal keyboard direction. The default is ltr, independent of ancestor direction. In RTL, ArrowLeft increases and ArrowRight decreases; ArrowUp, ArrowDown, Home and End retain their normal behavior. Star gaps use logical spacing in both directions.

<ReactStarsRating dir="rtl" defaultValue={2.5} ariaLabel="التقييم" />

Hover preview color

Set hoverColor to distinguish the pointer preview from both the saved value and inactive stars. It is opt-in, so omitting it preserves the original two-color presentation.

<ReactStarsRating
  value={3}
  primaryColor="#f59e0b"
  hoverColor="#38bdf8"
  secondaryColor="#374151"
/>

When the pointer previews 1.5, the first 1.5 stars use hoverColor, the remainder of the saved value uses primaryColor, and inactive stars use secondaryColor. When previewing above the saved value, only the additional preview region uses hoverColor.

Styling

The forwarded ref, className, style, title, and data-* attributes target the slider container. CSS variables provide lightweight theming:

<ReactStarsRating
  defaultValue={3.5}
  style={{
    '--stars-rating-primary-color': '#f59e0b',
    '--stars-rating-secondary-color': '#475569',
    '--stars-rating-focus-color': '#2563eb',
    '--stars-rating-size': '2rem',
    '--stars-rating-gap': '0.25rem',
  }}
/>

The default focus-visible ring uses the system Highlight color, and SVG stars allow browser forced-colors adjustments for high-contrast modes.

Server components

The package carries the 'use client' directive, so it can be imported directly from a server component in the Next.js App Router without a wrapper.

Form Integrations

Native HTML forms

Pass name to contribute a hidden value to FormData. defaultValue enables uncontrolled usage and native form reset behavior.

<form action="/reviews" method="post">
  <ReactStarsRating name="rating" defaultValue={3} />
  <button type="submit">Submit</button>
  <button type="reset">Reset</button>
</form>

Use value with onChange for controlled state. Supplying value always takes precedence over defaultValue. Use form="form-id" when the rating is rendered outside its associated form.

An uncanceled native reset restores defaultValue without calling onChange. Calling event.preventDefault() in the form's onReset preserves the rating. Controlled values remain the responsibility of the parent.

HTML hidden inputs do not participate in native constraint validation, so a required attribute would be misleading and is intentionally not exposed. For required ratings, validate the controlled value or submitted FormData and expose the result with aria-invalid and descriptive error text.

For example, inside a component, validate on submit and focus the rating when invalid (also validate submitted data on your server):

const [rating, setRating] = useState(0);
const [error, setError] = useState(false);
const ratingRef = useRef<HTMLSpanElement>(null);
const errorId = useId();

<form
  onSubmit={(event) => {
    event.preventDefault();
    const invalid = rating === 0;
    setError(invalid);
    if (invalid) ratingRef.current?.focus();
    else console.log('Submit rating:', rating);
  }}
>
  <ReactStarsRating
    ref={ratingRef}
    name="rating"
    value={rating}
    onChange={(value) => {
      setRating(value);
      setError(false);
    }}
    ariaLabel="Your rating (required)"
    aria-invalid={error || undefined}
    aria-describedby={error ? errorId : undefined}
  />
  {error && (
    <p id={errorId} role="alert">
      Choose a rating before submitting.
    </p>
  )}
  <button type="submit">Submit</button>
</form>;

Import useState, useRef and useId from React for this recipe.

React Hook Form

import { Controller, useForm } from 'react-hook-form';
import ReactStarsRating from 'react-awesome-stars-rating';

const { control } = useForm({ defaultValues: { rating: 3.5 } });

<Controller
  control={control}
  name="rating"
  render={({ field }) => (
    <ReactStarsRating
      ref={field.ref}
      value={field.value}
      onChange={field.onChange}
      onBlur={field.onBlur}
    />
  )}
/>;

Formik

import { Field } from 'formik';
import ReactStarsRating from 'react-awesome-stars-rating';

<Field name="rating">
  {({ field, form }) => (
    <ReactStarsRating
      value={field.value}
      onChange={(value) => form.setFieldValue('rating', value)}
      onBlur={() => form.setFieldTouched('rating', true)}
    />
  )}
</Field>;

TanStack Form

import { useForm } from '@tanstack/react-form';
import ReactStarsRating from 'react-awesome-stars-rating';

type FormValues = { rating: number };

const form = useForm<FormValues>({
  defaultValues: { rating: 3.5 },
  onSubmit: ({ value }) => {
    console.log('tanstack-form submit', value);
  },
});

<form
  onSubmit={(e) => {
    e.preventDefault();
    void form.handleSubmit();
  }}
>
  <form.Field name="rating">
    {(field) => (
      <ReactStarsRating
        value={field.state.value}
        onChange={field.handleChange}
        onBlur={field.handleBlur}
      />
    )}
  </form.Field>
</form>;

Development

Maintenance and feedback

The library is in maintenance mode: new feature development is paused while we collect real-world feedback. Bug fixes, accessibility reports and security updates remain welcome. Use the repository's bug and feature issue forms to include reproduction steps, versions and the user need behind a request.

Automated keyboard, axe and browser checks do not establish physical-device screen-reader compatibility. VoiceOver on iPhone and TalkBack on Android still need manual verification; see docs/accessibility-testing.md in the repository for the procedure and recorded results.

npm install
npm run dev          # demo app
npm run storybook    # Storybook
npm run test         # Vitest
npm run coverage     # Vitest with coverage thresholds
npm run lint         # ESLint
npm run format       # Prettier check
npm run typecheck    # TypeScript
npm run build        # library build (ESM + CJS + UMD + types) → dist/
npm run verify:package # render every entry point from dist/
npm run lint:package # publint + are-the-types-wrong on the packed tarball
npm run size         # size-limit budgets for every entry point
npm run test:e2e     # Chromium, Firefox and WebKit interaction/a11y tests
npm run verify:links # README HTTPS links (requires network)
npm run verify:site  # public demo + Storybook smoke tests (requires Chromium/network)
npm run build:site   # demo site → dist-site/

Pre-commit (Husky) runs lint-staged (Prettier + ESLint on staged files) and commitlint (Conventional Commits).

CI / Releases

  • CI — on push/PR to main: format, lint, commitlint, typecheck, tests, coverage, build, package verification, publint/attw, size budgets, Storybook and site builds.
  • Deploy — Vercel builds the demo site on every push (see vercel.json), serving Storybook at /storybook.
  • Releasesemantic-release on push to main: analyzes Conventional Commits, publishes to npm and creates a GitHub release.
  • Recovery — the manually dispatched Verify or repair a published release workflow verifies an exact npm version and its provenance, and can recreate a missing GitHub release only when its tag already exists.
  • Security — CodeQL and OpenSSF Scorecard publish findings to GitHub code scanning; dependency review checks pull requests for vulnerable dependencies.
  • Documentation health — changed documentation/check scripts, pushes to main, a weekly schedule and manual runs check README links and the deployed demo/Storybook. These external-service checks are separate from release gates. If npm's website blocks automated requests, the check explicitly reports that limitation and verifies the package through the registry instead.

Browser failures retain screenshots, traces and an HTML report in GitHub Actions artifacts for seven days. Local browser tests require npx playwright install. Development and release tooling use the Node 22 version in .nvmrc; consumer compatibility is tested separately.

Published-release verification cryptographically checks the Sigstore signature and transparency proofs, requires this repository's ci.yml identity on main, and checks the signed package digest against the downloaded tarball. The signed source commit must match the version's Git tag; release CI also requires it to match the current build commit. Verification never republishes the package.

Publishing uses npm trusted publishing (OIDC) from .github/workflows/ci.yml; the workflow does not use an npm token.

No manual tagging: merge to main with commits like feat: ... or fix: ... to trigger a release.

License

MIT