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

wts-star-rating

v1.0.1

Published

Accessible, dependency-free star rating and review score component for JavaScript, TypeScript, Angular, React, Vue, Svelte, and Web Components.

Readme

wts-star-rating

Accessible, dependency-free star rating, rating input, and review score component for vanilla JavaScript, TypeScript, Angular, React, Vue, Svelte, and Web Components. It supports fractional and half-star ratings, keyboard input, product-review interfaces, SSR, and an optional custom element.

The package is framework-agnostic by design:

  • the core is a small DOM controller with no runtime dependencies;
  • state can be read and updated without recreating the mount element;
  • callbacks and bubbling DOM events work with any UI framework;
  • destroy() provides deterministic listener and DOM cleanup;
  • importing either entry point is safe during server-side rendering;
  • the optional custom element is exported separately.

Install

npm install wts-star-rating

Import the stylesheet once:

import 'wts-star-rating/styles.css';

Controller API

<div id="product-rating"></div>
import { StarRating } from 'wts-star-rating';
import 'wts-star-rating/styles.css';

const rating = new StarRating({
  element: '#product-rating',
  value: 3.5,
  max: 5,
  step: 0.5,
  hoverEnabled: true,
  allowClear: true,
  ariaLabel: 'Product rating',
  formatValue: (value, max) => `${value} of ${max} stars`,
  onInput({ previewing, value }) {
    console.log(previewing ? 'preview' : 'restored', value);
  },
  onChange(detail) {
    console.log(detail.value, detail.source);
  },
});

rating.setValue(4); // silent, useful for syncing application state
rating.setValue(5, { emitChange: true });
rating.setReadonly(true);
console.log(rating.getValue());

// Call this when the owning view unmounts.
rating.destroy();

Only the DOM appended by the controller is removed by destroy(). Creating a second controller on the same element destroys the previous controller first.

To initialize every matching element, use the explicit collection helper:

import { createStarRatings } from 'wts-star-rating';

const ratings = createStarRatings('[data-rating]', {
  hoverEnabled: true,
  value: 2,
});

ratings.forEach((rating) => rating.destroy());

Web Component

Importing wts-star-rating/element registers <wts-star-rating> once. The registration is guarded when customElements is unavailable.

import 'wts-star-rating/element';
import 'wts-star-rating/styles.css';
<wts-star-rating
  aria-label="Product rating"
  allow-clear
  hover-enabled
  max="5"
  step="0.5"
  value="3.5"
></wts-star-rating>

The element exposes value, max, step, disabled, readOnly, controller, options, getValue(), setValue(), and destroy().

Framework examples

Create the controller after the framework has mounted the host element and destroy it from the matching cleanup hook.

Angular

import {
  afterNextRender,
  Component,
  DestroyRef,
  ElementRef,
  inject,
  viewChild,
} from '@angular/core';
import { StarRating } from 'wts-star-rating';
import 'wts-star-rating/styles.css';

@Component({
  selector: 'app-rating',
  template: '<div #host></div>',
})
export class RatingComponent {
  private readonly host = viewChild.required<ElementRef<HTMLElement>>('host');
  private readonly destroyRef = inject(DestroyRef);

  constructor() {
    afterNextRender(() => {
      const rating = new StarRating({
        element: this.host().nativeElement,
        value: 3,
        onChange: ({ value }) => console.log(value),
      });
      this.destroyRef.onDestroy(() => rating.destroy());
    });
  }
}

React

import { useEffect, useRef } from 'react';
import { StarRating } from 'wts-star-rating';
import 'wts-star-rating/styles.css';

export function Rating({ value, onChange }) {
  const host = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!host.current) return;
    const rating = new StarRating({
      element: host.current,
      value,
      onChange: ({ value: next }) => onChange(next),
    });
    return () => rating.destroy();
  }, []);

  return <div ref={host} />;
}

Call rating.setValue(value) from a second effect when the React prop can change. Programmatic updates are silent by default, preventing feedback loops.

Vue

<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from 'vue';
import { StarRating } from 'wts-star-rating';
import 'wts-star-rating/styles.css';

const host = ref<HTMLElement>();
let rating: StarRating | undefined;

onMounted(() => {
  rating = new StarRating({
    element: host.value!,
    value: 3,
    onChange: ({ value }) => console.log(value),
  });
});

onBeforeUnmount(() => rating?.destroy());
</script>

<template><div ref="host" /></template>

Options

| Option | Type | Default | Purpose | | --- | --- | --- | --- | | element | Element \| string | required | Mount element or selector | | value | number | 0 | Initial value | | max | number | 5 | Maximum rating | | step | number | 1 | Pointer, keyboard, and value increment | | hoverEnabled | boolean | false | Preview the pointer value | | allowClear | boolean | false | Clear by selecting the current value | | readonly | boolean | false | Present a non-interactive score | | disabled | boolean | false | Disable interaction | | showValue | boolean | true | Show the numeric value | | ariaLabel | string | "Rating" | Accessible control name | | formatValue | (value, max) => string | — | Format visible and ARIA value text | | onInput | (detail) => void | — | Receive hover previews | | onChange | (detail) => void | — | Receive committed changes |

setValue() clamps and snaps values to max and step. The controller also provides setOptions(), setReadonly(), enable(), disable(), and destroy().

Events

Committed changes emit:

  • star-rating-change, a bubbling CustomEvent<StarRatingChangeDetail>;
  • change, a bubbling native event;
  • the onChange callback, when supplied.

Hover previews emit the matching star-rating-input, input, and onInput notifications without changing getValue(). Their detail has previewing: true. Leaving the control or pressing Escape restores the committed value and emits one final input notification with previewing: false.

host.addEventListener('star-rating-change', (event) => {
  console.log(event.detail);
  // { value, previousValue, source, container }
});

Keyboard support includes arrow keys, Home, End, and—when allowClear is enabled—Delete and Backspace. Escape cancels a hover preview. Horizontal arrows and fractional pointer calculations automatically follow the computed direction, including dir="rtl" layouts.

Styling

Override CSS custom properties on the mount element:

.review-rating {
  --wts-star-rating-color: #ffb000;
  --wts-star-rating-empty-color: #d8d8d8;
  --wts-star-rating-focus-color: #155eef;
  --wts-star-rating-gap: 0.2rem;
  --wts-star-rating-size: 2.25rem;
}

Migration from the prototype

The old prototype metadata accidentally used the name wts-start-rating. This package uses the requested wts-star-rating name.

  • Replace elements: '.rating' with createStarRatings('.rating', options).
  • Import wts-star-rating/styles.css.
  • Call destroy() from framework cleanup hooks.
  • Use public setValue() or setOptions() for state synchronization.
  • The obsolete range-slider options declared by the prototype are removed.