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

@mshafiqyajid/react-rating

v0.3.0

Published

Headless star-rating hook and styled component for React. Half-step support, hover preview, full keyboard nav, custom icons, themable, SSR-safe, fully typed.

Downloads

568

Readme

@mshafiqyajid/react-rating

Full docs →

A polished star-rating component for React with half-step support, hover preview, and full keyboard nav. Comes in two flavors:

  • Styled<RatingStyled> with sizes, tones, label/hint, optional value display.
  • HeadlessuseRating() hook + unstyled <Rating> primitive. Bring your own icons (hearts, thumbs, anything).

Zero dependencies. SSR-safe. Fully typed. ESM + CJS.

Install

npm install @mshafiqyajid/react-rating

Peer dependency: react >= 17.

Quick start

Styled (recommended)

import { RatingStyled } from "@mshafiqyajid/react-rating/styled";
import "@mshafiqyajid/react-rating/styles.css";

export function Review() {
  const [value, setValue] = useState(0);
  return (
    <RatingStyled
      count={5}
      value={value}
      onChange={setValue}
      tone="warning"
      showValue
    />
  );
}

Headless

import { Rating } from "@mshafiqyajid/react-rating";

<Rating count={5} defaultValue={3.5} onChange={setValue} />

Hook (full custom UI)

import { useRating } from "@mshafiqyajid/react-rating";

function CustomRating() {
  const { items, value, displayValue, rootProps } = useRating({ count: 5 });
  return (
    <div {...rootProps}>
      {items.map((item) => (
        <span key={item.index} {...item.itemProps} style={item.style}>
          {/* item.fill is 0, 0.5, or 1 — render however you like */}
        </span>
      ))}
    </div>
  );
}

Recipes

Sizes and tones

<RatingStyled count={5} defaultValue={3.5} size="sm" tone="primary" />
<RatingStyled count={5} defaultValue={3.5} size="md" tone="warning" />   {/* default */}
<RatingStyled count={5} defaultValue={3.5} size="lg" tone="success" />

Available tones: neutral, primary, success, warning (golden — default), danger.

Half-step interaction

By default, hovering or clicking the left half of a star sets a half-rating; the right half sets a full rating. Disable for full-step only:

<RatingStyled count={5} allowHalf={false} />

Read-only display

<RatingStyled
  count={5}
  value={4.5}
  readOnly
  showValue
  label="Avg. rating"
  hint="Based on 248 reviews"
/>

Custom icon (hearts, thumbs, anything)

Pass any SVG. The component clones it twice (empty + filled layers) and clips the filled copy automatically.

<RatingStyled
  count={5}
  defaultValue={3}
  tone="danger"
  icon={
    <svg viewBox="0 0 24 24" fill="currentColor">
      <path d="M12 21s-7-4.35-7-10a4.5 4.5 0 0 1 8-2.83A4.5 4.5 0 0 1 19 11c0 5.65-7 10-7 10z" />
    </svg>
  }
/>

Higher star count

<RatingStyled count={10} defaultValue={7.5} size="sm" />

Async submit (Promise-driven pending state)

Return a Promise from onChange to drive data-pending automatically. Stars are non-interactive while the promise is in flight; on rejection the value reverts.

<RatingStyled
  count={5}
  defaultValue={0}
  onChange={async (next) => {
    await fetch("/api/reviews", {
      method: "POST",
      body: JSON.stringify({ stars: next }),
    });
  }}
/>

The hook also exposes isPending.

Show value badge

<RatingStyled
  count={5}
  defaultValue={3.5}
  showValue
  formatValue={(v, c) => `${v} / ${c}`}
/>

Theme via CSS variables

.brand-rating {
  --rrt-color-fill: #ec4899;
  --rrt-color-empty: #fce7f3;
  --rrt-icon-size: 2rem;
}
<RatingStyled count={5} className="brand-rating" />

API

<RatingStyled>

| Prop | Type | Default | Description | | ------------- | ------------------------------------------------------------- | ----------- | -------------------------------------------------------- | | count | number | 5 | Number of stars. | | value | number | — | Controlled value (0..count, in steps of 0.5 by default). | | defaultValue| number | 0 | Uncontrolled initial value. | | onChange | (value: number) => void | — | Fires on commit (click or keyboard). | | onHover | (value: number \| null) => void | — | Fires during hover preview. null on pointer leave. | | allowHalf | boolean | true | Enable half-step values. | | readOnly | boolean | false | Display only — no interaction. | | disabled | boolean | false | Disable interaction entirely. | | clearable | boolean | true | Re-clicking the active value clears it to 0. | | size | "sm" \| "md" \| "lg" | "md" | Size. | | tone | "neutral" \| "primary" \| "success" \| "warning" \| "danger" | "warning" | Color theme for filled stars. | | icon | ReactNode | star SVG | Custom icon (single node, used for both layers). | | showValue | boolean | false | Display the numeric value next to the stars. | | formatValue | (value, count) => ReactNode | — | Customize the value display (default: value.toFixed(1)). | | label | ReactNode | — | Label rendered above. | | hint | ReactNode | — | Helper text below. |

useRating(options)

Returns { value, hoverValue, displayValue, items, rootProps, setValue, clear }:

  • items[i].itemProps — spread onto your interactive element (role="radio", keyboard handlers, pointer handlers).
  • items[i].fill — 0 / 0.5 / 1 — the displayed fill amount (preview if hovering, committed value otherwise).
  • items[i].style — sets --rrt-fill for CSS-based clipping.
  • rootProps — spread onto the wrapper for role="radiogroup".

<Rating> (headless primitive)

Same options as the hook, plus:

  • renderIcon?: ({ index, fill, isHovered }) => ReactNode — fully custom per-star rendering.
  • icon?: ReactNode — single-node custom icon (used for both empty and filled layers).
  • children?: ({ items, value, hoverValue, displayValue, setValue, clear }) => ReactNode — full render-prop.

Keyboard nav

| Key | Action | | ------------------ | ------------------------------------------------- | | / | Decrease by step (0.5 or 1). | | / | Increase by step. | | Home | Set to 0. | | End | Set to count (max). | | Space / Enter | Set to focused star (or clear if already active and clearable). |

CSS variables

Override on .rrt-wrap, on the root via className, or on :root:

| Variable | Default | Description | | --------------------- | ------------ | --------------------------------- | | --rrt-color-fill | #f59e0b | Filled star color (varies by tone). | | --rrt-color-empty | #d4d4d8 | Empty star color. | | --rrt-color-hover-glow | golden glow | Focus ring tint. | | --rrt-icon-size | 1.5rem | Star size. | | --rrt-gap | 0.15rem | Gap between stars. | | --rrt-duration | 200ms | Transition duration. |

The styled component automatically:

  • Switches palette under prefers-color-scheme: dark
  • Disables animations under prefers-reduced-motion: reduce
  • Forwards ref to the root <div>

Browser support

Uses clip-path for half-fill (universally supported in evergreen browsers). SSR-safe — does nothing at import time.

License

MIT © Shafiq Yajid

Form integration

<form>
  <RatingStyled
    name="stars"
    label="Rating"
    hint="Click to rate"
    defaultValue={0}
    required
    error={errors.stars}
  />
</form>

| Prop | Type | Description | |---|---|---| | name | string | Renders a hidden input with the numeric value | | id | string | Wrapper id used for label association | | required | boolean | aria-required on the radiogroup + required on the hidden input | | error / invalid | ReactNode / boolean | Flips tone to danger and sets data-invalid |