React Rating

Basic usage

function App() {
  const [rating, setRating] = useState(3);

  return (
    <Rating
      style={{ maxWidth: 180 }}
      value={rating}
      onChange={setRating}
    />
  )
}
Container size

Basic usage - Read only

function App() {
  return (
    <Rating
      style={{ maxWidth: 180 }}
      value={3}
      readOnly
    />
  );
}

Disabled

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

  return (
    <Rating
      style={{ maxWidth: 180 }}
      value={rating}
      onChange={setRating}
      isDisabled
    />
  );
}

Required

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

  return (
    <Rating
      style={{ maxWidth: 180 }}
      value={rating}
      onChange={setRating}
      isRequired
    />
  );
}

Highlight only selected

function App() {
  const [rating, setRating] = useState(3);

  return (
    <Rating
      style={{ maxWidth: 180 }}
      value={rating}
      onChange={setRating}
      highlightOnlySelected
    />
  );
}

Reset button

function App() {
  const [rating, setRating] = useState(3);

  return (
    <div style={{ maxWidth: 180, width: '100%' }}>
      <Rating
        value={rating}
        onChange={setRating}
      />
      <button type="button" onClick={() => setRating(0)}>
        Reset
      </button>
    </div>
  );
}

Reset on click outside

function App() {
  const ratingRef = useRef(null);
  const [rating, setRating] = useState(0);
  
  useEffect(() => {
    const handleOutsideClick = (event) => {
      if (!ratingRef.current.contains(event.target)) {
        setRating(0);
      }
    };

    document.addEventListener('click', handleOutsideClick);
    return () => {
      document.removeEventListener('click', handleOutsideClick);
    };
  }, []);

  return (
    <Rating
      style={{ maxWidth: 180 }}
      ref={ratingRef}
      value={rating}
      onChange={setRating}
    />
  );
}

Custom feedback

function getRating(rating) {
  switch (rating) {
    case 1:
      return 'Poor';
    case 2:
      return 'Nothing special';
    case 3:
      return 'Average';
    case 4:
      return 'Very good';
    case 5:
      return 'Excellent';
    default:
      return 'None';
  }
}

function App() {
  const [rating, setRating] = useState(3);
  const [hoveredRating, setHoveredRating] = useState(0);

  return (
    <div style={{ maxWidth: 180, width: '100%' }}>
        <Rating value={rating} onChange={setRating} onHoverChange={setHoveredRating} />
        <div>
          <div>{`Selected: ${getRating(rating)}`}</div>
          <div>{`Hovered: ${getRating(hoveredRating)}`}</div>
        </div>
    </div>
  );
}
Selected: Average
Hovered: None

Asynchronous onChange

function App() {
  const [rating, setRating] = useState(3);
  const [isReadOnly, setIsReadOnly] = useState(false);

  async function handleAsyncSubmission(selectedValue) {
    try {
      setIsReadOnly(true);
      setRating(selectedValue);
      await new Promise((resolve) => setTimeout(() => resolve("Successfully submitted!"), 2000));
    } catch (err) {
      setIsReadOnly(false);
      setRating(0);
    }
  }
  
  return (
    <Rating
      style={{ maxWidth: 180 }}
      readOnly={isReadOnly}
      value={rating}
      onChange={handleAsyncSubmission}
    />
  );
}

Multiple ratings in a form

function App() {
  const [data, setData] = useState({
    name: '',
    cleanliness: 2,
    staff: 3,
    location: 0,
  });
  
  function handleSubmit(event) {
    event.preventDefault();
    alert(JSON.stringify(data, undefined, 2));
  }
  
  return (
    <form
      onSubmit={handleSubmit}
      maxWidth: 180,
      width: '100%',
    >
      <label htmlFor="name">
        Your name
          <input
            type="text"
            id="name"
            onChange={(event) =>
              setData((prevData) => ({ ...prevData, name: event.target.value }))
            }
          />
      </label>

      <div>
        <div id="cleanliness_rating">Cleanliness</div>
        <Rating
          isRequired
          value={data.cleanliness}
          visibleLabelId="cleanliness_rating"
          onChange={(selectedValue) =>
            setData((prevData) => ({ ...prevData, cleanliness: selectedValue }))
          }
        />
      </div>

      <div>
        <div id="staff_rating">Staff</div>
        <Rating
          isRequired
          value={data.staff}
          visibleLabelId="staff_rating"
          onChange={(selectedValue) =>
            setData((prevData) => ({ ...prevData, staff: selectedValue }))
          }
        />
      </div>

      <div>
        <div id="location_rating">Location</div>
        <Rating
          isRequired
          value={data.location}
          visibleLabelId="location_rating"
          onChange={(selectedValue) =>
            setData((prevData) => ({ ...prevData, location: selectedValue }))
          }
        />
      </div>

      <button type="submit">Submit review</button>
    </form>
  );
}
Cleanliness
Staff
Location

React Hook Form

function App() {
  const {
    register,
    handleSubmit,
    control,
    formState: { errors },
  } = useForm({
    mode: 'onBlur',
    defaultValues: {
      name: '',
      rating: 0,
    },
  });
  
  function onSubmit(data) {
    alert(JSON.stringify(data, undefined, 2));
  }
  
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label htmlFor="name">
        Your name
        <input type="text" id="name" {...register('name', { required: true })} />
      </label>
      {errors.name && <div>Name is required.</div>}

      <div>
        <div id="rating_label">Rating</div>
        <Controller
          control={control}
          name="rating"
          rules={{
            validate: (rating) => rating > 0,
          }}
          render={({ field: { onChange, onBlur, value } }) => (
            <Rating
              value={value}
              isRequired
              onChange={onChange}
              visibleLabelId="rating_label"
              onBlur={onBlur}
            />
          )}
        />
        {errors.rating && <div>Rating is required.</div>}
      </div>

      <button type="submit">
        Submit review
      </button>
    </form>
  );
}
Rating *

Included shapes

import { Rating, Star, ThinStar, RoundedStar, ThinRoundedStar, StickerStar } from '@smastrom/react-rating';

const includedShapesStyles = [Star, ThinStar, RoundedStar, ThinRoundedStar, StickerStar].map(
  (itemShapes) => ({ itemShapes, activeFillColor: '#f59e0b', inactiveFillColor: '#ffedd5' })
);

function App() {
  const [rating, setRating] = useState(3);

  return (
    <div>
    {includedShapesStyles.map((itemStyles, index) => (
      <Rating
        key={`shape_${index}`}
        value={rating}
        onChange={setRating}
        itemStyles={itemStyles}
      />
    ))}
    </div>
  )
}
Container size

Custom shape and colors

const StarDrawing = (
  <path
    d="M398.799,141.794c-43.394-3.977-86.776-6.52-130.158-8.418C258.835,99.302,242.633-4.751,193.173,0.169
      c-39.659,3.944-61.012,90.515-73.08,130.306c-32.333,0.283-64.692,1.062-97.09,2.416c-14.735,0.615-27.908,17.9-18.207,31.732
      c19.157,27.316,44.198,49.389,70.487,70.103c-11.83,38.196-21.665,77.499-29.759,116.53c-3.504,16.91-5.31,32.212,3.881,44.82
      c2.411,9.987,12.018,18.494,22.429,18.029c51.805-2.313,93.872-44.738,133.991-77.119c33.156,26.317,66.309,52.64,99.475,78.951
      c12.835,10.183,37.057,5.178,35.798-14.828c-3.039-48.158-15.477-96.473-30.599-144.041c32.951-25.229,65.899-50.459,99.11-75.353
      C426.818,168.817,420.858,143.814,398.799,141.794z"
  />
); // Source: https://www.svgrepo.com/svg/118939/star
  
const customStyles = {
  itemShapes: StarDrawing,
  activeFillColor: '#22C55E',
  inactiveFillColor: '#BBF7D0',
};

function App() {
  const [rating, setRating] = useState(4);

  return (
    <Rating
      style={{ maxWidth: 300 }}
      value={rating}
      onChange={setRating}
      itemStyles={customStyles}
    />
  );
}

Custom box colors

const Heart = (
  <path
    d="M433.5,67c-25.3-25.3-59-39.3-94.8-39.3s-69.6,14-94.9,39.4l-7.3,7.3l-7.5-7.5
    c-25.4-25.4-59.1-39.4-95-39.4c-35.8,0-69.4,13.9-94.7,39.3C13.9,92.2,0,125.9,0,161.7s14,69.5,39.4,94.8l182.7,182.7
    c3.8,3.8,9,6,14.5,6c5.4,0,10.6-2.2,14.5-6l182.2-182.4c25.4-25.4,39.3-59.1,39.4-94.9S458.8,92.4,433.5,67z M132.5,117.2
    c-23.9,0-43.4,19.5-43.4,43.4c0,11-8.9,19.9-19.9,19.9s-19.9-8.9-19.9-19.9c0-45.8,37.3-83.1,83.1-83.1c11,0,19.9,8.9,19.9,19.9
    C152.4,108.4,143.5,117.2,132.5,117.2z"
  />
); // Source: https://www.svgrepo.com/svg/40627/heart
  
const customStyles = {
  itemShapes: Heart,
  activeFillColor: 'white',
  activeBoxColor: '#EC4899',
  inactiveFillColor: 'white',
  inactiveBoxColor: '#FBCFE8',
};

function App() {
  const [rating, setRating] = useState(4);

  return (
    <Rating
      style={{ maxWidth: 360 }}
      value={rating}
      onChange={setRating}
      itemStyles={customStyles}
      transition="position"
      radius="medium"
      spaceInside="large"
      spaceBetween="small"
    />
  );
}

Custom active colors

const Star = (
  <path 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" />
); // Source: https://www.svgrepo.com/svg/353297/star
  
const customStyles = {
  itemShapes: Star,
  boxBorderWidth: 2,

  activeFillColor: ['#FEE2E2', '#FFEDD5', '#FEF9C3', '#ECFCCB', '#D1FAE5'],
  activeBoxColor: ['#da1600', '#db711a', '#dcb000', '#61bb00', '#009664'],
  activeBoxBorderColor: ['#c41400', '#d05e00', '#cca300', '#498d00', '#00724c'],

  inactiveFillColor: 'white',
  inactiveBoxColor: '#dddddd',
  inactiveBoxBorderColor: '#a8a8a8',
};

function App() {
  const [rating, setRating] = useState(4);

  return (
    <Rating
      style={{ maxWidth: 500 }}
      value={rating}
      onChange={setRating}
      itemStyles={customStyles}

      radius="large"
      spaceBetween="small"
      spaceInside="large"
    />
  );
}

Half-fill - SVG

const floatValues = [0.29, 1.44, 2.31, 3.48, 4.52];

function App() {
  return (
    <div>
      <div style={{ maxWidth: 180, width: '100%' }}>
        {floatValues.map((value) => (
          <Rating readOnly value={value} key={value} />
        ))}
      </div>
      <Rating
        style={{ maxWidth: 30 }}
        readOnly
        orientation="vertical"
        value={4.52}
      />
    </div>
  )
}

Half-fill - Box

import { Rating, ThinStar } from "@smastrom/react-rating";

const customStyles = {
  itemShapes: ThinStar,
  activeBoxColor: ['#e7040f', '#ff6300', '#ffde37', '#61bb00', '#19a974'],
  inactiveBoxColor: '#C7C7C7',
  inactiveFillColor: 'white',
};

const floatValues = [0.29, 1.44, 2.31, 3.48, 4.52];

function App() {
  return (
    <div>
      <div style={{ maxWidth: 180, width: '100%' }}>
        {floatValues.map((value) => (
          <Rating
            key={value}
            value={value}
            itemStyles={customStyles}
            readOnly
            spaceBetween="small"
            halfFillMode="box"
          />
        ))}
      </div>
      <Rating
        style={{ maxWidth: 30 }}
        readOnly
        value={4.52}
        itemStyles={customStyles}
        orientation="vertical"
        halfFillMode="box"
        spaceBetween="small"
      />
    </div>
  )
}

Faces rating

// Collection source: https://www.svgrepo.com/collection/fluent-ui-icons-outlined

const SadFace = (
  <path 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" />
);

const NeutralFace = (
  <>
    <path d="M9.00051 8.75122C9.69047 8.75122 10.2498 9.31055 10.2498 10.0005C10.2498 10.6905 9.69047 11.2498 9.00051 11.2498C8.31055 11.2498 7.75122 10.6905 7.75122 10.0005C7.75122 9.31055 8.31055 8.75122 9.00051 8.75122Z" />
    <path d="M15.0005 8.75122C15.6905 8.75122 16.2498 9.31055 16.2498 10.0005C16.2498 10.6905 15.6905 11.2498 15.0005 11.2498C14.3105 11.2498 13.7512 10.6905 13.7512 10.0005C13.7512 9.31055 14.3105 8.75122 15.0005 8.75122Z" />
    <path d="M8.25 15C7.83579 15 7.5 15.3358 7.5 15.75C7.5 16.1642 7.83579 16.5 8.25 16.5H15.75C16.1642 16.5 16.5 16.1642 16.5 15.75C16.5 15.3358 16.1642 15 15.75 15H8.25Z" />
    <path d="M2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12ZM12 3.5C7.30558 3.5 3.5 7.30558 3.5 12C3.5 16.6944 7.30558 20.5 12 20.5C16.6944 20.5 20.5 16.6944 20.5 12C20.5 7.30558 16.6944 3.5 12 3.5Z" />
  </>
);

const SmilingFace = (
  <path 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" />
);

const HappyFace = (
  <>
    <path d="M6.74927 12C6.53852 12 6.33749 12.0887 6.19539 12.2443C6.05329 12.4 5.98323 12.6082 6.00237 12.8181C6.28259 15.8916 8.55224 18.5 12 18.5C15.4478 18.5 17.7174 15.8916 17.9977 12.8181C18.0168 12.6082 17.9468 12.4 17.8047 12.2443C17.6626 12.0887 17.4616 12 17.2508 12H6.74927ZM12 17C9.74286 17 8.12852 15.5205 7.63237 13.5H16.3677C15.8715 15.5205 14.2571 17 12 17Z" />
    <path d="M15.2501 8.75C14.8416 8.75 14.5398 9.03719 14.492 9.35982C14.4314 9.76957 14.05 10.0526 13.6403 9.99192C13.2305 9.93126 12.9475 9.54993 13.0082 9.14018C13.1696 8.0495 14.1313 7.25 15.2501 7.25C16.3689 7.25 17.3306 8.0495 17.492 9.14018C17.5527 9.54993 17.2697 9.93126 16.8599 9.99192C16.4502 10.0526 16.0688 9.76957 16.0082 9.35982C15.9604 9.03719 15.6586 8.75 15.2501 8.75Z" />
    <path d="M7.99202 9.35982C8.03977 9.03719 8.34157 8.75 8.7501 8.75C9.15863 8.75 9.46043 9.03719 9.50818 9.35982C9.56884 9.76957 9.95017 10.0526 10.3599 9.99192C10.7697 9.93126 11.0527 9.54993 10.992 9.14018C10.8306 8.0495 9.86892 7.25 8.7501 7.25C7.63128 7.25 6.66963 8.0495 6.50818 9.14018C6.44753 9.54993 6.73053 9.93126 7.14028 9.99192C7.55003 10.0526 7.93136 9.76957 7.99202 9.35982Z" />
    <path d="M12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2ZM3.5 12C3.5 7.30558 7.30558 3.5 12 3.5C16.6944 3.5 20.5 7.30558 20.5 12C20.5 16.6944 16.6944 20.5 12 20.5C7.30558 20.5 3.5 16.6944 3.5 12Z" />
  </>
);

const customStyles = {
  itemShapes: [SadFace, NeutralFace, SmilingFace, HappyFace],
  activeFillColor: ['#da1600', '#dcb000', '#61bb00', '#009664'],
  inactiveFillColor: '#a8a8a8',
};

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

  return (
    <Rating
      style={{ maxWidth: 300 }}
      value={rating}
      onChange={setRating}
      itemStyles={customStyles}
      items={4}
      highlightOnlySelected
      spaceBetween="medium"
      transition="zoom"
    />
  );
}

Upvote / Downvote

const dAttr = `M13.915,0.379l8.258,9.98c0,0,1.252,1.184-0.106,1.184c-1.363,0-4.653,0-4.653,0s0,0.801,0,2.025
c0,3.514,0,9.9,0,12.498c0,0,0.184,0.709-0.885,0.709c-1.072,0-5.783,0-6.55,0c-0.765,0-0.749-0.592-0.749-0.592
c0-2.531,0-9.133,0-12.527c0-1.102,0-1.816,0-1.816s-2.637,0-4.297,0c-1.654,0-0.408-1.24-0.408-1.24s7.025-9.325,8.001-10.305
C13.24-0.414,13.915,0.379,13.915,0.379z`; // Source: https://www.svgrepo.com/svg/35304/up-arrow

const ArrowUp = <path d={dAttr} />;
const ArrowDown = <path transform="rotate(180 0 0)" d={dAttr} />;
  
const customStyles = {
  itemShapes: [ArrowUp, ArrowDown],
  boxBorderWidth: 2,
  activeFillColor: ['#16A34A', '#E11D48'],
  activeBoxBorderColor: ['#16A34A', '#E11D48'],
  inactiveFillColor: '#9CA3AF',
  inactiveBoxBorderColor: '#9CA3AF',
};

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

  return (
    <div>
      <Rating
        style={{ maxWidth: 60 }}
        value={rating}
        onChange={setRating}
        itemStyles={customStyles}
        items={2}
        highlightOnlySelected
        radius="full"
        orientation="vertical"
        spaceBetween="medium"
        spaceInside="large"
        invisibleLabel="Upvote or downvote this post"
        invisibleItemLabels={['Upvote', 'Downvote']}
      />
      <div>{rating === 1 && 'Upvoted'}</div>
      <div>{rating === 2 && 'Downvoted'}</div>
    </div>
  );
}

Custom invisible labels

const CUSTOM_GROUP_LABEL = 'Rate GitHub Plaza Hotel';
const CUSTOM_LABELS = ['Bad', 'Poor', 'Average', 'Very Good', 'Excellent'];

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

  return (
    <Rating
      style={{ maxWidth: 200 }}
      value={rating}
      itemStyles={customStyles}
      onChange={(selectedValue) => setRating(selectedValue)}
      invisibleLabel={CUSTOM_GROUP_LABEL}
      invisibleItemLabels={CUSTOM_ITEM_LABELS}
      spaceBetween="small"
      spaceInside="medium"
    />
  );
}

Custom visible labels

const CUSTOM_GROUP_LABEL = 'Rate GitHub Plaza Hotel';
const CUSTOM_GROUP_LABEL_ID = 'group_label';

const CUSTOM_ITEM_LABELS = ['Bad', 'Poor', 'Average', 'Very Good', 'Excellent'];
const CUSTOM_ITEM_LABELS_IDS = ['label_1', 'label_2', 'label_3', 'label_4', 'label_5'];

function App() {
  const [rating, setRating] = useState(4);

  return (
    <div role="group", style={{ maxWidth: 450, width: '100%' }}>
      <div id={CUSTOM_GROUP_LABEL_ID}>{CUSTOM_GROUP_LABEL}</div>
      <Rating
        value={rating}
        itemStyles={customStyles}
        onChange={setRating}
        visibleLabelId={CUSTOM_GROUP_LABEL_ID}
        visibleItemLabelIds={CUSTOM_ITEM_LABELS_IDS}
        spaceBetween="medium"
        spaceInside="medium"
        transition="colors"
      />
      <div
        style={{
          display: 'grid',
          gridTemplateColumns: 'repeat(5, 1fr)',
          justifyItems: 'center',
        }}
      >
        {CUSTOM_ITEM_LABELS.map((label, index) => (
          <span
            key={label}
            id={CUSTOM_ITEM_LABELS_IDS[index]}
            style={{
              opacity: index + 1 === rating ? 1 : 0.35,
              textDecoration: index + 1 === rating ? 'underline' : 'inherit',
              padding: '0 5%',
            }}
          >
            {label}
          </span>
        ))}
      </div>
    </div>
  );
}
Rate GitHub Plaza Hotel
BadPoorAverageVery GoodExcellent

Custom mixed labels

const CUSTOM_GROUP_LABEL = 'Rate GitHub Plaza Hotel';
const CUSTOM_GROUP_LABEL_ID = 'group_label';

const CUSTOM_ITEM_LABELS = ['Bad', 'Poor', 'Average', 'Very Good', 'Excellent'];

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

  return (
    <div role="group" style={{ maxWidth: 400, width: 100% }}>
      <div id={CUSTOM_GROUP_LABEL_ID}>{CUSTOM_GROUP_LABEL}</div>
      <Rating
        value={rating}
        itemStyles={customStyles}
        onChange={setRating}
        visibleLabelId={CUSTOM_GROUP_LABEL_ID}
        invisibleItemLabels={CUSTOM_ITEM_LABELS}
        spaceBetween="small"
        spaceInside="medium"
        transition="colors"
      />
    </div>
  );
}
Rate GitHub Plaza Hotel

Custom CSS

.custom-classname {
  gap: 5px;
  max-width: 200px;
  width: 100%;
}

.custom-classname .rr--svg {
  padding: 5px;
  border-width: 1px;
  border-style: solid;
  border-radius: 4px;
}

.custom-classname .rr--on .rr--svg {
  fill: MediumSeaGreen;
  background: MintCream;
  border-color: MediumSeaGreen;
}

.custom-classname .rr--off .rr--svg {
  fill: Tomato;
  background: AntiqueWhite;
  border-color: Tomato;
}

@media (min-width: 610px) {
  .custom-classname {
    max-width: 300px;
    gap: 10px;
  }

  .custom-classname .rr--svg {
    padding: 10px;
    border-width: 2px;
    border-radius: 10px;
  }
}

import { Star } from "react-rating-input";

import './custom-styles.css';

// 1. Omit the properties styled via CSS
const customStyles = {
  itemShapes: Star;
};

export function App() {
  return (
    <Rating
      readOnly
      value={3}
      itemStyles={customStyles}
      className="custom-classname"
      // 2. Set to "none" any prop as well
      transition="none"
      spaceInside="none"
      radius="none"
    />
  );
}

Right to left

function App() {
  const [rating, setRating] = useState(2);

  return (
    <div dir="rtl" style={{ maxWidth: 180, width: '100%' }}>
      <div>
        <Rating
          value={rating}
          onChange={setRating}
        />
        {`Selected value: ${rating}`}
      </div>
      <Rating readOnly value={2.42} />
      <Rating
        readOnly
        value={3.52}
        halfFillMode="box"
        itemStyles={customStyles}
        spaceBetween="small"
      />
    </div>
  );
}
Selected value: 2