{"id":13422128,"url":"https://github.com/smastrom/react-rating","last_synced_at":"2025-05-16T12:08:02.613Z","repository":{"id":41365718,"uuid":"487513700","full_name":"smastrom/react-rating","owner":"smastrom","description":"⭐ Zero-dependency, highly customizable rating component for React.","archived":false,"fork":false,"pushed_at":"2024-06-19T00:26:45.000Z","size":642,"stargazers_count":367,"open_issues_count":4,"forks_count":6,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-01T19:07:48.480Z","etag":null,"topics":["rating","rating-star","rating-stars","react","react-component","react-rating","reviews","star","stars"],"latest_commit_sha":null,"homepage":"https://reactrating.netlify.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/smastrom.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"polar":"smastrom","buy_me_a_coffee":"smastrom"}},"created_at":"2022-05-01T11:01:05.000Z","updated_at":"2025-03-25T20:08:29.000Z","dependencies_parsed_at":"2024-01-25T05:09:30.102Z","dependency_job_id":"0b3b7815-9536-4203-a4ef-a32aaf3dc19d","html_url":"https://github.com/smastrom/react-rating","commit_stats":{"total_commits":133,"total_committers":4,"mean_commits":33.25,"dds":0.09022556390977443,"last_synced_commit":"97de32d3db5e8658537e660e43d99bb36cfbd8f1"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smastrom%2Freact-rating","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smastrom%2Freact-rating/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smastrom%2Freact-rating/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smastrom%2Freact-rating/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/smastrom","download_url":"https://codeload.github.com/smastrom/react-rating/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247987285,"owners_count":21028895,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["rating","rating-star","rating-stars","react","react-component","react-rating","reviews","star","stars"],"created_at":"2024-07-30T23:00:37.570Z","updated_at":"2025-04-09T06:09:21.075Z","avatar_url":"https://github.com/smastrom.png","language":"TypeScript","funding_links":["https://polar.sh/smastrom","https://buymeacoffee.com/smastrom"],"categories":["UI Components","TypeScript"],"sub_categories":["Form Components"],"readme":"![react-rating-version](https://img.shields.io/npm/v/@smastrom/react-rating?color=22C55E) ![react-rating-build-workflow](https://img.shields.io/github/actions/workflow/status/smastrom/react-rating/build.yml?branch=main\u0026color=22C55E)\n![react-rating-tests-workflow](https://img.shields.io/github/actions/workflow/status/smastrom/react-rating/tests.yml?branch=main\u0026color=22C55E\u0026label=tests) ![react-rating-coverage](https://img.shields.io/codecov/c/github/smastrom/react-rating?color=22C55E) ![react-rating-dependencies](https://img.shields.io/badge/dependency%20count-0-22C55E)\n\n# React Rating\n\nZero dependency, highly customizable rating component for React.\n\n![react-rating](https://i.ibb.co/L6M0hfw/new.png)\n\n[Demo and Examples](https://reactrating.netlify.app) — [NextJS Page Router](https://stackblitz.com/edit/nextjs-5qw9id?file=pages/index.tsx) — [Vite](https://stackblitz.com/edit/vitejs-vite-gwqytd?file=src/App.tsx)\n\n\u003cbr /\u003e\n\n## Features\n\n- **Use any SVG**: No headaches, icon fonts or packages to install in order to use your favorite shapes.\n- Endless possibilities of customization\n- Most common rating shapes included\n- Zero-config smart half-fill\n- Dead simple per-active-item styling\n- Built with accessibility in mind\n- Truly responsive and mobile-first\n- Controllable with React Hook Form\n- Simple DOM structure\n- Zero-config RTL support\n- Works with SSR\n\n\u003cbr/\u003e\n\n## Installation\n\n```zsh\npnpm add @smastrom/react-rating\n```\n\n```zsh\nyarn add @smastrom/react-rating\n```\n\n```zsh\nnpm i @smastrom/react-rating\n```\n\n\u003cbr /\u003e\n\n## Usage\n\n### 1. Import CSS and Rating component\n\n```jsx\nimport { Rating } from '@smastrom/react-rating'\n\nimport '@smastrom/react-rating/style.css'\n```\n\n\u003e Importing the CSS **only once** (most likely _main.js_ or _App.jsx_) is enough to use Rating in any other component of your App.\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eRemix\u003c/strong\u003e\u003c/summary\u003e\n\u003cbr /\u003e\n\n**app/root.tsx**\n\n```tsx\nimport styles from '@smastrom/react-rating/style.css';\n\nexport function links() {\n  return [{ rel: 'stylesheet', href: styles }];\n}\n\nexport default function App() {\n  // ...\n```\n\n**in any page.tsx**\n\n```tsx\nimport { Rating } from '@smastrom/react-rating';\n\nexport default function Index() {\n  // ...\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eNextJS 13 - App Router\u003c/strong\u003e\u003c/summary\u003e\n\n### Interactive rating\n\n**app/layout.tsx**\n\n```tsx\nimport '@smastrom/react-rating/style.css'\n```\n\n**components/Rating.tsx**\n\n```tsx\n'use client'\n\nimport { useState } from 'react'\nimport { Rating as ReactRating } from '@smastrom/react-rating'\n\nexport function Rating() {\n  const [rating, setRating] = useState(0)\n\n  return \u003cReactRating style={{ maxWidth: 100 }} value={rating} onChange={setRating} /\u003e\n}\n```\n\n**in any page/component:**\n\n```tsx\nimport { Rating } from './components/Rating'\n\nexport default function Home() {\n  return (\n    \u003cdiv\u003e\n      {/* Other nodes... */}\n      \u003cRating /\u003e\n      {/* Other nodes... */}\n    \u003c/div\u003e\n  )\n}\n```\n\n### Non-interactive rating\n\n**app/layout.tsx**\n\n```tsx\nimport '@smastrom/react-rating/style.css'\n```\n\n**in any page/component:**\n\n```tsx\nimport { Rating } from '@smastrom/react-rating'\n\nexport default function Home() {\n  return (\n    \u003cdiv\u003e\n      {/* Other nodes... */}\n      \u003cRating style={{ maxWidth: 100 }} value={3} readOnly /\u003e\n      {/* Other nodes... */}\n    \u003c/div\u003e\n  )\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eNextJS - Pages Router\u003c/strong\u003e\u003c/summary\u003e\n\n\u003cbr /\u003e\n\n**pages/\\_app.js**\n\n```jsx\nimport '@smastrom/react-rating/style.css';\n\nfunction MyApp({ Component, pageProps }) {\n  // ...\n```\n\n**in any page/component:**\n\n```tsx\nimport { Rating } from '@smastrom/react-rating';\n\nexport default function Home() {\n  // ...\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eGatsby\u003c/strong\u003e\u003c/summary\u003e\n\n\u003cbr /\u003e\n\n**gatsby-browser.js** - Create the file at the root of your project if it doesn't exist, and relaunch the dev server.\n\n```jsx\nimport '@smastrom/react-rating/style.css'\n```\n\n**in any page/component:**\n\n```tsx\nimport { Rating } from '@smastrom/react-rating';\n\nconst IndexPage = () =\u003e {\n  // ...\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eVite\u003c/strong\u003e\u003c/summary\u003e\n\n\u003cbr /\u003e\n\n**main.jsx**\n\n```jsx\nimport '@smastrom/react-rating/style.css'\n```\n\n**in any component:**\n\n```jsx\nimport { Rating } from '@smastrom/react-rating';\n\nfunction App() {\n  // ...\n```\n\n\u003c/details\u003e\n\n\u003cbr /\u003e\n\n### 2. Give it a max-width and init the state\n\nSince **Rating** will span across the entire container, define a _maximum width_ via inline styles or css class:\n\n```jsx\nfunction App() {\n  const [rating, setRating] = useState(0) // Initial value\n\n  return \u003cRating style={{ maxWidth: 250 }} value={rating} onChange={setRating} /\u003e\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eCSS Class\u003c/strong\u003e\u003c/summary\u003e\n\u003cbr /\u003e\n\n**my-styles.css**\n\n```css\n.my-rating-class {\n  max-width: 600px;\n}\n```\n\n**App.jsx**\n\n```jsx\nimport './my-styles.css'\n\nfunction App() {\n  const [rating, setRating] = useState(0)\n\n  return \u003cRating className=\"my-rating-class\" value={rating} onChange={setRating} /\u003e\n}\n```\n\n\u003c/details\u003e\n\n\u003cbr /\u003e\n\n## API\n\n**Legend**\n\n| Color               | Description                                |\n| ------------------- | ------------------------------------------ |\n| :green_circle:      | Has always effect                          |\n| :large_blue_circle: | Has effect only if `readOnly` is **false** |\n| :purple_circle:     | Has effect only if `readOnly` is **true**  |\n\n\u003cbr /\u003e\n\n### :cyclone: Core\n\n| :thinking:          | Prop             | Description                                                                    | Type                                            | Default   | Required                        |\n| ------------------- | ---------------- | ------------------------------------------------------------------------------ | ----------------------------------------------- | --------- | ------------------------------- |\n| :green_circle:      | `value`          | An integer from 0 to `items`. It can be a float if `readOnly` is **true**.     | number                                          | undefined | :white_check_mark:              |\n| :large_blue_circle: | `onChange`       | Setter or custom function to update the rating.                                | RatingChange                                    | () =\u003e {}  | Only if `readOnly` is **false** |\n| :large_blue_circle: | `onHoverChange`  | Callback to execute while navigating the rating items.                         | (hoveredValue: number) =\u003e void                  | () =\u003e {}  | :x:                             |\n| :green_circle:      | `items`          | Rating items to display.                                                       | 1 \\| 2 \\| 3 \\| 4 \\| 5 \\| 6 \\| 7 \\| 8 \\| 9 \\| 10 | 5         | :x:                             |\n| :green_circle:      | `readOnly`       | Whether to render an accessible image element or not.                          | boolean                                         | false     | :x:                             |\n| :large_blue_circle: | `isDisabled`     | Whether to disable the radio group or not.                                     | boolean                                         | false     | :x:                             |\n| :large_blue_circle: | `isRequired`     | Whether users should be able to set rating to 0 or not.                        | boolean                                         | false     | :x:                             |\n| :green_circle:      | `preventDefault` | Whether or not to call `event.preventDefault` on click and Enter/Space select. | `click` \\| `keydown` \\| `all` \\| `none`         | `all`     | :x:                             |\n\n`ref`, `id`, `className`, `style`, `onBlur`, `onFocus` are also available.\n\n\u003cbr /\u003e\n\n### :nail_care: Appearance\n\n| :thinking:          | Prop                    | Description                                                | Type                                                    | Default       | Required |\n| ------------------- | ----------------------- | ---------------------------------------------------------- | ------------------------------------------------------- | ------------- | -------- |\n| :green_circle:      | `highlightOnlySelected` | Whether to highlight only the selected rating item or not. | boolean                                                 | false         | :x:      |\n| :purple_circle:     | `halfFillMode`          | Whether to half-fill the shape or the bounding box.        | `svg` \\| `box`                                          | `svg`         | :x:      |\n| :green_circle:      | `itemStyles`            | Custom shapes and colors.                                  | ItemStyles                                              | defaultStyles | :x:      |\n| :green_circle:      | `spaceInside`           | Responsive padding of each rating item.                    | `none` \\| `small` \\| `medium` \\| `large`                | `small`       | :x:      |\n| :green_circle:      | `spaceBetween`          | Responsive gap between rating items.                       | `none` \\| `small` \\| `medium` \\| `large`                | `none`        | :x:      |\n| :green_circle:      | `radius`                | Responsive radius of the bounding box.                     | `none` \\| `small` \\| `medium` \\| `large` \\| `full`      | `none`        | :x:      |\n| :green_circle:      | `orientation`           | Orientation of the rating items.                           | `horizontal` \\| `vertical`                              | `horizontal`  | :x:      |\n| :large_blue_circle: | `transition`            | Transition to apply when hovering/selecting.               | `none` \\| `zoom` \\| `colors` \\| `opacity` \\| `position` | `colors`      | :x:      |\n\n\u003cbr /\u003e\n\n### :open_umbrella: Accessibility\n\n| :thinking:          | Prop                  | Description                                                                                                       | Type     | Default                                                                          | Required |\n| ------------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------- | -------- |\n| :green_circle:      | `invisibleLabel`      | Accessible label of the rating group / image.                                                                     | string   | • `Rating Selection` (radioGroup) \u003cbr /\u003e • `Rated {value} on {items}` (readOnly) | :x:      |\n| :large_blue_circle: | `invisibleItemLabels` | Accessible labels of each rating item.                                                                            | string[] | `Rate 1`, `Rate 2`...                                                            | :x:      |\n| :large_blue_circle: | `visibleLabelId`      | DOM ID of the element used as rating group label. If set, takes precedence over `invisibleLabel`.                 | string   | undefined                                                                        | :x:      |\n| :large_blue_circle: | `visibleItemLabelIds` | DOM IDs of the elements used as labels for each rating item. If set, takes precedence over `invisibleItemLabels`. | string[] | undefined                                                                        | :x:      |\n| :large_blue_circle: | `resetLabel`          | Accessible label of the reset radio button.                                                                       | string   | `Reset Rating`                                                                   | :x:      |\n\n\u003cbr /\u003e\n\n## onChange\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eType Definition\u003c/strong\u003e\u003c/summary\u003e\n\u003cbr /\u003e\n\n```ts\ntype RatingChange =\n  | React.Dispatch\u003cReact.SetStateAction\u003cnumber\u003e\u003e\n  | ((selectedValue: number) =\u003e void | Promise\u003cvoid\u003e)\n```\n\n\u003c/details\u003e\n\n### Basic\n\nIf your app doesn't require any custom logic/state to set the rating, you can simply pass the setter to `onChange`:\n\n```js\nfunction App() {\n  const [rating, setRating] = useState(0)\n\n  return \u003cRating value={rating} onChange={setRating} /\u003e\n}\n```\n\n### Custom logic/state\n\nIf you need to perform actions while setting the rating (like calling an API) or you need to update a complex state, `onChange` accepts a callback whose only parameter equals to the selected rating:\n\n```js\nfunction App() {\n  const [state, setState] = useState({\n    review: '',\n    rating: 0 // Initial value\n  })\n\n  function handleChange(selectedValue) {\n    // 1. Logs the selected rating (1, 2, 3...)\n    console.log(selectedValue)\n\n    // 2. Do something with or without the value...\n\n    // 3. Update Rating UI\n    setState((prevState) =\u003e ({\n      ...prevState,\n      rating: selectedValue\n    }))\n  }\n\n  return \u003cRating onChange={handleChange} value={state.rating} /\u003e\n}\n```\n\n\u003cbr /\u003e\n\n## Behavior\n\n### 1. Rating with reset - _Default_\n\nBy default, the user is able to reset the rating (from 1-5 to 0 and vice versa):\n\n| Interaction | Reset action                                  | Preview                                                             |\n| ----------- | --------------------------------------------- | ------------------------------------------------------------------- |\n| Mouse       | By clicking again on the selected rating item | ![react-rating](https://i.ibb.co/pLPP1wM/ezgif-com-gif-maker-2.gif) |\n| Keyboard    | By navigating to an invisible reset radio     | ![react-rating](https://i.ibb.co/3YWM7Fx/ezgif-com-gif-maker-1.gif) |\n\n\u003e :bulb: Don't like the default focus style? Check [here](#troubleshooting) how to customize it.\n\n### 2. Rating without reset\n\nThere could be scenarios where you want to force the user to express a rating _(e.g. review page, post-service rating)_.\n\nIn such cases, set `isRequired` to **true**:\n\n```jsx\n\u003cRating isRequired value={rating} onChange={setRating} /\u003e\n```\n\n![react-rating](https://i.ibb.co/BrtwWPX/ezgif-com-gif-maker-4.gif)\n\n- It is not possible to reset by clicking again on the selected rating or by using the invisible radio.\n\n- It is announced by screen readers that rating **is required**.\n\n- If value equals to 0, it is announced by screen readers that rating **is invalid** .\n\n\u003e :bulb: Don't like the default focus style? Check [here](#troubleshooting) how to customize it.\n\n\u003cbr/\u003e\n\n## Styling\n\n### Using included shapes\n\nThis package ships with six of the most common (open source) rating shapes that you can import and use:\n\n| Import Name       | Preview                                                                          | Collection / Author                                          |\n| ----------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------ |\n| `Star`            | ![react-rating](https://i.ibb.co/0jS3F2P/Schermata-2022-09-29-alle-09-45-48.png) | [Feather](https://feathericons.com/)                         |\n| `ThinStar`        | ![react-rating](https://i.ibb.co/9hzfsmJ/Schermata-2022-10-01-alle-00-25-39.png) | [Raphael](https://github.com/dmitrybaranovskiy/raphael)      |\n| `RoundedStar`     | ![react-rating](https://i.ibb.co/V9P422w/Schermata-2022-09-30-alle-23-47-02.png) | [Phosphor](https://phosphoricons.com/)                       |\n| `ThinRoundedStar` | ![react-rating](https://i.ibb.co/tP3fRfz/Schermata-2022-09-30-alle-23-59-46.png) | [SVG Repo](https://www.svgrepo.com/svg/99804/star-favourite) |\n| `StickerStar`     | ![react-rating](https://i.ibb.co/C2sPq9X/Schermata-2022-10-01-alle-00-30-48.png) | [Raphael](https://www.svgrepo.com/svg/99804/star-favourite)  |\n| `Heart`           | ![react-rating](https://i.ibb.co/7gvN66m/Schermata-2022-09-29-alle-10-26-24.png) | [Feather](https://feathericons.com/)                         |\n\n```jsx\nimport { Rating, ThinStar } from '@smastrom/react-rating'\n\n// Declare it outside your component so it doesn't get re-created\nconst myStyles = {\n  itemShapes: ThinStar,\n  activeFillColor: '#ffb700',\n  inactiveFillColor: '#fbf1a9'\n}\n\nfunction App() {\n  const [rating, setRating] = useState(0)\n\n  return (\n    \u003cRating style={{ maxWidth: 300 }} value={rating} onChange={setRating} itemStyles={myStyles} /\u003e\n  )\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eCustomizable properties\u003c/strong\u003e\u003c/summary\u003e\n\n\u003cbr/\u003e\n\nYou can pass an object with the following properties to `itemStyles` prop:\n\n```ts\ntype ItemStyles = {\n  itemShapes: JSX.Element | JSX.Element[]\n\n  itemStrokeWidth?: number\n  boxBorderWidth?: number\n\n  activeFillColor?: string | string[]\n  activeStrokeColor?: string | string[]\n  activeBoxColor?: string | string[]\n  activeBoxBorderColor?: string | string[]\n\n  inactiveFillColor?: string\n  inactiveStrokeColor?: string\n  inactiveBoxColor?: string\n  inactiveBoxBorderColor?: string\n}\n```\n\nBesides `itemShapes`, **all the properties are optional**. If a property isn't defined, no classes nor CSS variables will be added to the SVG.\n\nJust set the ones you need and that's it:\n\n```jsx\nconst CustomStar = (\n  \u003cpolygon points=\"478.53 189 318.53 152.69 239.26 0 160 152.69 0 189 111.02 303.45 84 478.53 239.26 396.63 394.53 478.53 367.51 303.45 478.53 189\" /\u003e\n)\n\nconst myStyles = {\n  itemShapes: CustomStar,\n  activeFillColor: '#22C55E',\n  inactiveFillColor: '#BBF7D0'\n}\n\nfunction App() {\n  const [rating, setRating] = useState(4)\n\n  return (\n    \u003cRating style={{ maxWidth: 300 }} value={rating} onChange={setRating} itemStyles={myStyles} /\u003e\n  )\n}\n```\n\n\u003c/details\u003e\n\n\u003cbr /\u003e\n\n### Using your own shapes\n\nAll you have to do is to open the SVG with a text editor, grab the \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Basic_Shapes\"\u003einner shapes\u003c/a\u003e and delete any attribute except for \u003ca href=\"https://www.w3.org/TR/SVG/geometry.html\"\u003egeometric\u003c/a\u003e and \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/transform\"\u003etransform\u003c/a\u003e ones.\n\nIf the SVG comes from quality sources such as any collection you can find on [Icônes](https://icones.js.org/collection/all), all you have to do is to delete a couple of fill and stroke attributes (if any):\n\n```html\n\u003csvg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"\u003e\n  \u003cpath\n    fill=\"currentColor\"\n    stroke=\"2\"\n    stroke-linecap=\"round\"\n    stroke-linejoin=\"round\"\n    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\"\n  /\u003e\n\u003c/svg\u003e\n```\n\nThen define a JSX element to render the shapes. Rating will take care of rendering a brand-new, responsive SVG for you:\n\n```jsx\nconst CustomStar = (\n  \u003cpath 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\" /\u003e\n)\n\nconst myStyles = {\n  itemShapes: CustomStar,\n  itemStrokeWidth: 2,\n  activeFillColor: 'LightSeaGreen',\n  activeStrokeColor: '#99F6E4',\n  inactiveFillColor: '#99F6E4',\n  inactiveStrokeColor: 'LightSeaGreen'\n}\n\nfunction App() {\n  const [rating, setRating] = useState(4)\n\n  return (\n    \u003cRating style={{ maxWidth: 300 }} value={rating} onChange={setRating} itemStyles={myStyles} /\u003e\n  )\n}\n```\n\n\u003cbr /\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eCustomizable properties\u003c/strong\u003e\u003c/summary\u003e\n\n\u003cbr/\u003e\n\nYou can pass an object with the following properties to `itemStyles` prop:\n\n```ts\ntype ItemStyles = {\n  itemShapes: JSX.Element | JSX.Element[]\n\n  itemStrokeWidth?: number\n  boxBorderWidth?: number\n\n  activeFillColor?: string | string[]\n  activeStrokeColor?: string | string[]\n  activeBoxColor?: string | string[]\n  activeBoxBorderColor?: string | string[]\n\n  inactiveFillColor?: string\n  inactiveStrokeColor?: string\n  inactiveBoxColor?: string\n  inactiveBoxBorderColor?: string\n}\n```\n\nBesides `itemShapes`, **all the properties are optional**. If a property isn't defined, no classes nor CSS variables will be added to the SVG.\n\nJust set the ones you need and that's it:\n\n```jsx\nconst CustomStar = (\n  \u003cpolygon points=\"478.53 189 318.53 152.69 239.26 0 160 152.69 0 189 111.02 303.45 84 478.53 239.26 396.63 394.53 478.53 367.51 303.45 478.53 189\" /\u003e\n)\n\nconst myStyles = {\n  itemShapes: CustomStar,\n  activeFillColor: '#22C55E',\n  inactiveFillColor: '#BBF7D0'\n}\n\nfunction App() {\n  const [rating, setRating] = useState(4)\n\n  return (\n    \u003cRating style={{ maxWidth: 300 }} value={rating} onChange={setRating} itemStyles={myStyles} /\u003e\n  )\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eDefault styles\u003c/strong\u003e\u003c/summary\u003e\n\u003cbr /\u003e\n\n```js\nimport { Star } from '@smastrom/react-rating'\n\nconst defaultItemStyles = {\n  itemShapes: Star,\n  itemStrokeWidth: 2,\n  activeFillColor: '#ffb23f',\n  activeStrokeColor: '#e17b21',\n  inactiveFillColor: '#fff7ed',\n  inactiveStrokeColor: '#e17b21'\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eHow itemStrokeWidth works\u003c/strong\u003e\u003c/summary\u003e\n\u003cbr /\u003e\n\nThe stroke width is expressed in _viewBox user coordinate's unit size_ and **not in pixels**.\n\nDepending on the vector nodes provided you may have to input and try different values in order to reach the desired stroke width.\n\nIt is responsive by nature, so expect it to increase/decrease when resizing the container.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eColor values\u003c/strong\u003e\u003c/summary\u003e\n\u003cbr /\u003e\n\nYou can pass any valid CSS color string such as `aliceblue`, `#FFF332`, `rgba(0, 0, 0, 0)` or `hsl(69, 22, 200)`.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eTypeScript\u003c/strong\u003e\u003c/summary\u003e\n\u003cbr /\u003e\n\n```tsx\nimport type { ItemStyles } from '@smastrom/react-rating'\n\nconst Star = (\n  \u003cpolygon points=\"478.53 189 318.53 152.69 239.26 0 160 152.69 0 189 111.02 303.45 84 478.53 239.26 396.63 394.53 478.53 367.51 303.45 478.53 189\" /\u003e\n)\n\nconst myStyles: ItemStyles = {\n  itemShapes: Star,\n  activeFillColor: 'green',\n  inactiveFillColor: 'gray'\n}\n```\n\n\u003cbr /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eQuick guide for complex or messy SVGs\u003c/strong\u003e\u003c/summary\u003e\n\n1. Keep only the \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/SVG/Element/g\"\u003egroups\u003c/a\u003e and the inner shapes of the svg: `g`, `path`, `circle`, `rect`, `polygon`, `ellipse`, `polyline` or `line` and delete any other node (e.g. `\u003cdefs\u003e`).\n\n2. If a group is present, check if it has the `transform` attribute set. If the attribute is not set, keep the inner shapes and delete the `g` node.\n\n3. Delete any attribute **except** geometric, draw and \u003cu\u003e\u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/transform\"\u003etransform\u003c/a\u003e\u003c/u\u003e ones from any group and shape.\n\n4. If present, delete any empty node like `\u003ccircle\u003e\u003c/circle\u003e` or `\u003cg\u003e\u003c/g\u003e`.\n\n\u003c/details\u003e\n\n\u003cbr /\u003e\n\n### Per-active-item styling\n\nIf you wish to style each rating item, you can optionally pass an array of JSX elements to `itemShapes` and an array of valid CSS colors to any **\u003cu\u003eactive\u003c/u\u003e** property:\n\n![react-rating](https://i.ibb.co/QXsDp8B/Schermata-2022-06-30-alle-01-30-51.png)\n\n```jsx\nconst SadFace = (\n  \u003cpath d=\"M12.0000002,1.99896738 C17.523704,1.99896738 22.0015507,6.47681407 22.0015507,12.0005179 C22.0015507,17.5242217 17.523704,22.0020684 12.0000002,22.0020684 C6.47629639,22.0020684 1.99844971,17.5242217 1.99844971,12.0005179 C1.99844971,6.47681407 6.47629639,1.99896738 12.0000002,1.99896738 Z M12.0000002,3.49896738 C7.30472352,3.49896738 3.49844971,7.30524119 3.49844971,12.0005179 C3.49844971,16.6957946 7.30472352,20.5020684 12.0000002,20.5020684 C16.6952769,20.5020684 20.5015507,16.6957946 20.5015507,12.0005179 C20.5015507,7.30524119 16.6952769,3.49896738 12.0000002,3.49896738 Z M12.0000001,13.4979816 C13.6312483,13.4979816 15.1603686,14.1528953 16.2810488,15.2934358 C16.5713583,15.5888901 16.5671876,16.0637455 16.2717333,16.354055 C15.976279,16.6443646 15.5014236,16.6401939 15.211114,16.3447396 C14.3696444,15.4883577 13.2246935,14.9979816 12.0000001,14.9979816 C10.7726114,14.9979816 9.62535029,15.4905359 8.78347552,16.3502555 C8.49366985,16.6462041 8.01882223,16.6511839 7.72287367,16.3613782 C7.4269251,16.0715726 7.4219453,15.5967249 7.71175097,15.3007764 C8.83296242,14.155799 10.3651558,13.4979816 12.0000001,13.4979816 Z M9.00044779,8.75115873 C9.69041108,8.75115873 10.2497368,9.3104845 10.2497368,10.0004478 C10.2497368,10.6904111 9.69041108,11.2497368 9.00044779,11.2497368 C8.3104845,11.2497368 7.75115873,10.6904111 7.75115873,10.0004478 C7.75115873,9.3104845 8.3104845,8.75115873 9.00044779,8.75115873 Z M15.0004478,8.75115873 C15.6904111,8.75115873 16.2497368,9.3104845 16.2497368,10.0004478 C16.2497368,10.6904111 15.6904111,11.2497368 15.0004478,11.2497368 C14.3104845,11.2497368 13.7511587,10.6904111 13.7511587,10.0004478 C13.7511587,9.3104845 14.3104845,8.75115873 15.0004478,8.75115873 Z\" /\u003e\n)\n\nconst SmilingFace = (\n  \u003cpath d=\"M12.0000002,1.99896738 C17.523704,1.99896738 22.0015507,6.47681407 22.0015507,12.0005179 C22.0015507,17.5242217 17.523704,22.0020684 12.0000002,22.0020684 C6.47629639,22.0020684 1.99844971,17.5242217 1.99844971,12.0005179 C1.99844971,6.47681407 6.47629639,1.99896738 12.0000002,1.99896738 Z M12.0000002,3.49896738 C7.30472352,3.49896738 3.49844971,7.30524119 3.49844971,12.0005179 C3.49844971,16.6957946 7.30472352,20.5020684 12.0000002,20.5020684 C16.6952769,20.5020684 20.5015507,16.6957946 20.5015507,12.0005179 C20.5015507,7.30524119 16.6952769,3.49896738 12.0000002,3.49896738 Z M8.46174078,14.7838355 C9.31087697,15.8615555 10.6018926,16.5020843 11.9999849,16.5020843 C13.396209,16.5020843 14.6856803,15.8632816 15.5349376,14.7880078 C15.7916692,14.4629512 16.2633016,14.4075628 16.5883582,14.6642944 C16.9134148,14.9210259 16.9688032,15.3926584 16.7120717,15.717715 C15.5813083,17.1494133 13.8601276,18.0020843 11.9999849,18.0020843 C10.1373487,18.0020843 8.41411759,17.1471146 7.28351576,15.7121597 C7.02716611,15.3868018 7.08310832,14.9152347 7.40846617,14.6588851 C7.73382403,14.4025354 8.20539113,14.4584777 8.46174078,14.7838355 Z M9.00044779,8.75115873 C9.69041108,8.75115873 10.2497368,9.3104845 10.2497368,10.0004478 C10.2497368,10.6904111 9.69041108,11.2497368 9.00044779,11.2497368 C8.3104845,11.2497368 7.75115873,10.6904111 7.75115873,10.0004478 C7.75115873,9.3104845 8.3104845,8.75115873 9.00044779,8.75115873 Z M15.0004478,8.75115873 C15.6904111,8.75115873 16.2497368,9.3104845 16.2497368,10.0004478 C16.2497368,10.6904111 15.6904111,11.2497368 15.0004478,11.2497368 C14.3104845,11.2497368 13.7511587,10.6904111 13.7511587,10.0004478 C13.7511587,9.3104845 14.3104845,8.75115873 15.0004478,8.75115873 Z\" /\u003e\n)\n\nconst myStyles = {\n  itemShapes: [SadFace, SmilingFace],\n  activeFillColor: ['#da1600', '#61bb00'],\n  inactiveFillColor: '#a8a8a8'\n}\n\nfunction App() {\n  const [rating, setRating] = useState(0)\n\n  return (\n    \u003cRating\n      style={{ maxWidth: 200 }}\n      value={rating}\n      onChange={setRating}\n      items={2}\n      itemStyles={myStyles}\n      highlightOnlySelected\n    /\u003e\n  )\n}\n```\n\n![react-rating](https://s8.gifyu.com/images/in_AdobeExpress.gif)\n\n```jsx\nconst Star = (\n  \u003cpath d=\"M62 25.154H39.082L32 3l-7.082 22.154H2l18.541 13.693L13.459 61L32 47.309L50.541 61l-7.082-22.152L62 25.154z\" /\u003e\n)\n\nconst myStyles = {\n  itemShapes: Star,\n  boxBorderWidth: 3,\n\n  activeFillColor: ['#FEE2E2', '#FFEDD5', '#FEF9C3', '#ECFCCB', '#D1FAE5'],\n  activeBoxColor: ['#da1600', '#db711a', '#dcb000', '#61bb00', '#009664'],\n  activeBoxBorderColor: ['#c41400', '#d05e00', '#cca300', '#498d00', '#00724c'],\n\n  inactiveFillColor: 'white',\n  inactiveBoxColor: '#dddddd',\n  inactiveBoxBorderColor: '#a8a8a8'\n}\n\nfunction App() {\n  const [rating, setRating] = useState(0)\n\n  return (\n    \u003cRating\n      style={{ maxWidth: 500 }}\n      value={rating}\n      onChange={setRating}\n      itemStyles={myStyles}\n      radius=\"large\"\n      spaceInside=\"large\"\n    /\u003e\n  )\n}\n```\n\n\u003cbr /\u003e\n\n### Half-fill and float values\n\nIf `readOnly` is set to **true**, the `value` prop accepts a float:\n\n```jsx\n\u003cRating readOnly value={1.38} /\u003e\n```\n\nThe component will try to round it to the nearest half integer:\n\n```js\n3.2 = 3\n3.26 = 3.5\n3.62 = 3.5\n3.75 = 4\n```\n\n\u003e :warning: The value will only be rounded \"internally\" for graphical purposes. The accessible label will always display the value provided.\n\nIf necessary, the SVG will be half-filled by default (`halfFillMode = 'svg'`):\n\n![react-rating](https://i.ibb.co/V2kJ317/Screenshot-2022-11-13-alle-12-07-51.png)\n\nAll the boxes will have the same background color (inactiveBoxColor) and `activeBoxColor` will have no effect.\n\nYou can switch between `svg` and `box`:\n\n```jsx\n\u003cRating readOnly value={2.38} halfFillMode=\"box\" /\u003e\n```\n\n![react-rating](https://i.ibb.co/sKpybbV/Schermata-2022-06-01-alle-23-43-29.png)\n\nIn this case instead, all the shapes will have the same fill color (inactiveFillColor) and `activeFillColor` will have no effect.\n\nIf you don't want the half-fill feature, simply pass an integer to `value`.\n\n\u003e :warning: If `highlightOnlySelected` is set to **true**, no half-fill will take place.\n\n\u003cbr /\u003e\n\n## Accessibility\n\n### Mouse/keyboard callbacks\n\nReact Rating leverages `aria radiogroup` role instead of native HTML radio buttons in order to improve keyboard-users experience and extend capabilities (e.g. API calls) by keeping callbacks consistent between keyboard and mouse.\n\nIn **React Rating**:\n\n- Rating must be confirmed with `Enter/Space` keys and cannot be set directly with arrows like native radios.\n\n- `onChange` is called on both `Enter/Space` keys and click.\n\n- `onHoverChange` is called on `← → ↑ ↓` navigation, mouse hovering, focus-from / blur-to a _non-react-rating_ element.\n\n### Disabled state\n\nIt is always announced by screen readers instead of being completely hidden.\n\n### Labels\n\nReact Rating ships with default accessible labels computed from your `items` value. In order to customize them or to switch to visible ones check the multiple examples on the [demo website](https://reactrating.netlify.app).\n\n\u003cbr /\u003e\n\n## Troubleshooting\n\n### I can see the nodes returned from rendering, but no styles have been applied.\n\nCheck that you are importing the CSS as displayed in the [Basic usage](#basic-usage) section.\n\n### I keep getting the error: \"itemShapes is not a valid JSX element\".\n\nCheck that you are passing a JSX element and not a functional component:\n\n:white_check_mark: **Correct**\n\n```jsx\nconst Star = \u003cpath d=\"M100,10L40 198 190 78 10 78 160 198z\" /\u003e\n```\n\n:x: **Incorrect**\n\n```jsx\nconst Star = () =\u003e \u003cpath d=\"M100,10L40 198 190 78 10 78 160 198z\" /\u003e\n```\n\n### I passed an array of shapes but the stroke width looks different for each item.\n\nWhen passing different shapes for each rating item, this package forces you to use icons from the same collection to keep design consistency. Be sure you are doing that.\n\nYou can find clean, SVG collections at [Icônes](https://icones.js.org/collection/all).\n\n### I don't like the default focus ring styles. How can I style them?\n\nIt is possible to style them via CSS by targeting the following selectors:\n\n**Rating items**\n\n```css\n.rr--box:focus-visible .rr--svg {\n  /* Your styles */\n}\n```\n\n**Reset**\n\n```css\n.rr--focus-reset {\n  /* Your styles */\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eDefaults\u003c/strong\u003e\u003c/summary\u003e\n\n```css\n.rr--focus-reset {\n  outline: 6px double #0079ff;\n}\n\n.rr--box:focus-visible .rr--svg {\n  outline: 6px double #0079ff;\n}\n```\n\n\u003c/details\u003e\n\n\u003cbr /\u003e\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmastrom%2Freact-rating","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsmastrom%2Freact-rating","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmastrom%2Freact-rating/lists"}