react-rating

repository·main·Indexed 18 days ago

https://github.com/smastrom/react-rating

A zero-dependency, highly customizable rating component for React. It supports custom SVG shapes, smart half-fills, and is built with accessibility and responsiveness. Features include interactive and read-only modes, configurable rating items (1-10), and support for custom item styles and shapes.

Tokens
7.5K
Snippets
17
Records
26
Agent score
62%

What's inside @smastrom/react-rating

  1. Understand the accessibility model in React Rating

    main

    React Rating uses the aria radiogroup role to provide a consistent experience for both mouse and keyboard users.

    Keyboard Interaction

    • Selection: Ratings must be confirmed using the Enter or Space keys. Unlike native HTML radio buttons, they cannot be set directly using arrow keys.
    • Callbacks:
      • onChange is triggered by both Enter/Space and mouse clicks.
      • onHoverChange is triggered by arrow key navigation (← → ↑ ↓), mouse hovering, or when focus moves from/to an element outside the rating component.

    Screen Reader Support

    • Disabled State: When the component is disabled, the state is announced by screen readers rather than being hidden.
    • Labels: The component generates default accessible labels based on the items value. These can be customized or switched to visible labels via props (refer to the demo website for implementation details).
  2. Configure rating reset behavior with isRequired

    main

    The Rating component has two distinct behaviors regarding whether a user can reset their selection to 0:

    1. Rating with reset (Default)

    By default, users can toggle between a rating (1-5) and 0 (no rating).

    • Mouse: Clicking the currently selected rating item again resets it to 0.
    • Keyboard: Navigating to an invisible reset radio allows resetting.

    2. Rating without reset (Required)

    To force a user to select a rating (e.g., for a mandatory review), set the isRequired prop to true.

    • Behavior: Users cannot reset the rating by clicking the selected item or using the invisible radio.
    • Accessibility: Screen readers will announce that the rating is required and, if the value is 0, that the rating is invalid.
    <Rating isRequired value={rating} onChange={setRating} />
  3. Handle half-fill and float values

    main

    When the readOnly prop is set to true, the value prop can accept float values. The component will round the value internally for graphical purposes (e.g., 3.26 becomes 3.5), but the accessible label will always display the exact value provided.

    There are two modes for half-filling:

    1. svg mode (default): The SVG itself is half-filled. In this mode, all boxes will have the same background color (inactiveBoxColor), and activeBoxColor will have no effect.
    2. box mode: The shapes will have the same fill color (inactiveFillColor), and activeFillColor will have no effect. Use halfFillMode="box" to switch.

    Important Constraints:

    • If highlightOnlySelected is set to true, no half-fill will take place.
    • To avoid half-fill entirely, pass an integer to value.
    // Example of readOnly with float value
    <Rating readOnly value={1.38} />
    
    // Example of readOnly with box-based half-fill
    <Rating readOnly value={2.38} halfFillMode="box" />
  4. Import CSS and the Rating component

    main

    To use the component, you must import the Rating component and its associated CSS. You only need to import the CSS once (e.g., in your main.js, App.jsx, or root layout file) for it to be available throughout your application.

    import { Rating } from '@smastrom/react-rating'
    import '@smastrom/react-rating/style.css'
  5. Use custom SVG shapes in Rating

    main

    You can use your own SVG shapes by defining them as JSX elements. To ensure compatibility, open your SVG in a text editor and keep only the inner shapes (path, circle, rect, polygon, ellipse, polyline, or line) and any necessary <g> (group) tags. Delete all attributes except for geometric and transform attributes. If using a group, ensure it has a transform attribute; otherwise, keep only the inner shapes.

    Note: itemStrokeWidth is expressed in viewBox user coordinate units, not pixels, and is responsive by nature.

    const CustomStar = (
      <path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z" />
    )
    
    const myStyles = {
      itemShapes: CustomStar,
      itemStrokeWidth: 2,
      activeFillColor: 'LightSeaGreen',
      activeStrokeColor: '#99F6E4',
      inactiveFillColor: '#99F6E4',
      inactiveStrokeColor: 'LightSeaGreen'
    }
    
    function App() {
      const [rating, setRating] = useState(4)
    
      return (
        <Rating style={{ maxWidth: 300 }} value={rating} onChange={setRating} itemStyles={myStyles} />
      )
    }
  6. Use included rating shapes

    main

    The package provides six common rating shapes that can be imported directly from @smastrom/react-rating. You can use these shapes by assigning them to the itemShapes property within the itemStyles object passed to the Rating component.

    import { Rating, ThinStar } from '@smastrom/react-rating'
    
    const myStyles = {
      itemShapes: ThinStar,
      activeFillColor: '#ffb700',
      inactiveFillColor: '#fbf1a9'
    }
    
    function App() {
      const [rating, setRating] = useState(0)
    
      return (
        <Rating style={{ maxWidth: 300 }} value={rating} onChange={setRating} itemStyles={myStyles} />
      )
    }
  7. Fix inconsistent stroke widths in rating items

    main
    If you pass an array of different shapes and notice inconsistent stroke widths, ensure that all icons belong to the same SVG collection. The package enforces design consistency by assuming icons share similar properties. Using a single collection (e.g., from Icônes) is recommended.
  8. Fix 'itemShapes is not a valid JSX element' error

    main

    This error occurs when you pass a functional component instead of a direct JSX element to the itemShapes prop. You must pass the evaluated JSX element itself.

    Correct approach:

    const Star = <path d="M100,10L40 198 190 78 10 78 160 198z" />

    Incorrect approach:

    const Star = () => <path d="M100,10L40 198 190 78 10 78 160 198z" />
  9. Create an interactive rating component

    main

    To create an interactive rating, manage the rating value in your component's state and pass the setter to the onChange prop. Because the Rating component spans the entire width of its container, you should define a maxWidth via inline styles or a CSS class to control its size.

    import { useState } from 'react'
    import { Rating } from '@smastrom/react-rating'
    
    function App() {
      const [rating, setRating] = useState(0)
    
      return <Rating style={{ maxWidth: 250 }} value={rating} onChange={setRating} />
    }
  10. Create a non-interactive rating component

    main

    To display a rating that cannot be changed by the user, use the readOnly prop. In readOnly mode, the value can be a float (for partial ratings).

    import { Rating } from '@smastrom/react-rating'
    
    function App() {
      return <Rating style={{ maxWidth: 100 }} value={3} readOnly />
    }