How to make a Rating Component with React & Typescript
Tags: webdev, javascript, react, typescript
Published: 2023-01-19 09:28:40

Creating a custom rating component in React using Typescript looks like intimidating sometimes, but it's pretty simple to do. In this article, I will show you how to create a custom Rating component using React & Typescript step by step.


![Custom Rating Component](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6273og2in3vz3xa62bs3.gif)



To start, you will need to create a new React project and make sure that TypeScript is set up. You can use [create-react-app](https://create-react-app.dev/docs/getting-started) or [vite](https://vitejs.dev/guide/) to quickly set up a new project with TypeScript. 

Once you have your project set up, create a new file for your rating component and name it like "Rating.tsx" and follow these steps one by one. 

**Step 1:** Define the props for the component. We will use the following props to customize our components.

- `className`: a className that can be passed to customize the styling of the component.
- `count`: the number of stars to display in the rating.
- `value`: the current value of the rating.
- `color`: the color of the stars when they are not being hovered over or clicked on.
- `hoverColor`: the color of the stars when they are being hovered over.
- `activeColor`: the color of the stars when they are being clicked on.
- `size`: the size of the stars in pixels.
- `edit`: a boolean value that determines whether the rating can be edited by the user.
- `isHalf`: a boolean value that determines whether the rating allows for half-star increments.
- `onChange`: a callback function that is called when the rating is changed.
- `emptyIcon`: an icon to be used for empty stars.
- `halfIcon`: an icon to be used for half stars.
- `fullIcon`: an icon to be used for full stars.

In your `Rating.tsx` file: 

```ts
interface RatingProps {
  className?: string;
  count: number;
  value: number;
  color?: string;
  hoverColor?: string;
  activeColor?: string;
  size?: number;
  edit?: boolean;
  isHalf?: boolean;
  onChange?: (value: number) => void;
  emptyIcon?: React.ReactElement;
  halfIcon?: React.ReactElement;
  fullIcon?: React.ReactElement;
}
```
**Step 2:** Define the star icon components using `svg`. You can also use any icon library for that.

```tsx
interface IconProps {
  size?: number;
  color?: string;
}

const FullStar = ({ size = 24, color = "#000000" }: IconProps) => {
  return (
    <div style={{ color: color }}>
      <svg height={size} viewBox="0 0 24 24">
        <path
          d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"
          fill="currentColor"
        />
        <path d="M0 0h24v24H0z" fill="none" />
      </svg>
    </div>
  );
};

const HalfStar = ({ size = 24, color = "#000000" }: IconProps) => {
  return (
    <div style={{ color: color }}>
      <svg height={size} viewBox="0 0 24 24">
        <path
          d="M22 9.24l-7.19-.62L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27 18.18 21l-1.63-7.03L22 9.24zM12 15.4V6.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.4z"
          fill="currentColor"
        />
        <path d="M0 0h24v24H0z" fill="none" />
      </svg>
    </div>
  );
};

const EmptyStar = ({ size = 24, color = "#000000" }: IconProps) => {
  return (
    <div style={{ color: color }}>
      <svg height={size} viewBox="0 0 24 24">
        <path
          d="M22 9.24l-7.19-.62L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27 18.18 21l-1.63-7.03L22 9.24zM12 15.4l-3.76 2.27 1-4.28-3.32-2.88 4.38-.38L12 6.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.4z"
          fill="currentColor"
        />
        <path d="M0 0h24v24H0z" fill="none" />
      </svg>
    </div>
  );
};

```

**Step 4:** Now, define the rating component, destruct the props with some default values. 

```tsx
const Rating: React.FC<RatingProps> = ({
  className,
  count,
  value,
  color = "#ffd700",
  hoverColor = "#ffc107",
  activeColor = "#ffc107",
  size = 30,
  edit = false,
  isHalf = true,
  onChange,
  emptyIcon = <EmptyStar />,
  halfIcon = <HalfStar />,
  fullIcon = <FullStar />
}) => {
  return <div></div>
};
```

**Step 5:** Next, create a state for our rating component. This state will be used to track the current rating value selected by the user. We will also need to define some methods to update the state when the user interacts with the component.

```ts
const [hoverValue, setHoverValue] = useState<number | undefined>(undefined);

const handleMouseMove = (index: number) => {
  if (!edit) {
    return;
  }
  setHoverValue(index);
};

const handleMouseLeave = () => {
  if (!edit) {
    return;
  }
  setHoverValue(undefined);
};

const handleClick = (index: number) => {
  if (!edit) {
    return;
  }
  if (onChange) {
    onChange(index + 1);
  }
};
```

**Step 6:** Now we have the props and state set up, you can start rendering the stars for the rating component. For doing this, we will need to use a loop to render the number of stars specified by the `count` prop. Inside the loop, we need to check whether the current star should be empty, half or full. We should also add the `onMouseEnter`, `onMouseLeave` and `onClick` events along with the `size` and `color` to each of these star icon components.

```tsx
 const stars = [];

  for (let i = 0; i < count; i++) {
    let star: React.ReactElement;
    if (isHalf && value - i > 0 && value - i < 1) {
      star = halfIcon;
    } else if (i < value) {
      star = fullIcon;
    } else {
      star = emptyIcon;
    }

    if (hoverValue !== undefined) {
      if (i <= hoverValue) {
        star = fullIcon;
      }
    }

    stars.push(
      <div
        key={i}
        style={{ cursor: "pointer" }}
        onMouseMove={() => handleMouseMove(i)}
        onMouseLeave={handleMouseLeave}
        onClick={() => handleClick(i)}
      >
        {React.cloneElement(star, {
          size: size,
          color: i <= Number(hoverValue) ? hoverColor : i < value ? activeColor : color,
        })}
      </div>
    );
  }

 return <div className={`rating ${className}`}>{stars}</div>;
```

**Step 7:** Finally, export the component so that it can be used in other parts of application. Here is an example use of our rating component: 

```tsx
import React from "react";
import Rating from "./Rating";

export default function App() {
  const [rating, setRating] = React.useState(0);

  return (
    <div className="App">
      <div>
        <h3>Editable</h3>
        <Rating
          count={5}
          value={rating}
          edit={true}
          onChange={(value) => setRating(value)}
        />
        <p>
          <b>Value: </b>
          {rating} stars
        </p>
      </div>
    </div>
  );
}

```
Check out a demo of this component in action by visiting this link [CodeSandbox](https://codesandbox.io/s/react-rating-component-s06xj4?file=/src/App.tsx:0-713)