Design Converter
Education
Last updated on Jun 27, 2024
Last updated on Nov 9, 2023
In web applications, user feedback is a cornerstone of user experience (UX) design. Among the various feedback mechanisms, the rating system is a direct and intuitive way for users to express their opinions.
In this blog, we'll delve into creating a React rating component. This feature has become ubiquitous in e-commerce sites, review platforms, and anywhere user preferences are valuable.
With its component-based architecture, React offers a seamless way to integrate such interactive elements into your applications. By the end of this post, you'll clearly understand how to build a customizable and accessible React rating component that users will love to interact with.
Before we begin crafting our React rating component, let's ensure we have everything we need:
A React rating component allows users to provide a visual rating, typically using stars, by selecting a value that reflects their satisfaction or preference. This component is not just about collecting feedback; it's also about enhancing user engagement and providing a visual cue that can influence other users' decisions.
The first step in building our React rating component is to define its structure. We'll start by creating a new React component and adding the necessary JSX:
1import React, { useState } from 'react'; 2import './RatingComponent.css'; // Assuming we're using plain CSS 3 4const RatingComponent = () => { 5 return ( 6 <div className="rating-container"> 7 {/* Stars will be rendered here */} 8 </div> 9 ); 10}; 11 12export default RatingComponent; 13
For styling, whether you choose plain CSS or a utility-first CSS framework like Tailwind CSS, the goal is to create a visually appealing and interactive set of stars that users can click on to set their rating.
Our rating component will be interactive, so we need to manage its state. We'll use the useState hook to keep track of the current rating:
1const RatingComponent = () => { 2 const [currentRating, setCurrentRating] = useState(0); 3 4 // Rest of the component 5}; 6
The currentRating state will hold the value of the rating the user selects, and we'll update it using the setCurrentRating function.
To display the stars, we'll create an array representing each star and map over it to render the star icons:
1const RatingComponent = () => { 2 const [currentRating, setCurrentRating] = useState(0); 3 const totalStars = 5; 4 const stars = Array(totalStars).fill(0); 5 6 return ( 7 <div className="rating-container"> 8 {stars.map((_, index) => ( 9 <span 10 key={index} 11 className={`star ${index < currentRating ? 'filled' : ''}`} 12 onClick={() => setCurrentRating(index + 1)} 13 > 14 ☆ 15 </span> 16 ))} 17 </div> 18 ); 19}; 20
In this example, we're using a simple star symbol (☆) for our icons, but you can replace this with SVGs or font icons for more style.
To make our stars responsive to user clicks, we'll add an onClick event that updates the currentRating state:
1const handleRating = (ratingValue) => { 2 setCurrentRating(ratingValue); 3}; 4 5// Inside the render method 6<span onClick={() => handleRating(index + 1)}> 7 ☆ 8</span> 9
This handleRating function captures the rating value and updates the state, which updates the displayed rating.
Customization is key for a reusable component. We'll add props to our component to allow developers to define the maximum rating, the size of the stars, and the fill color:
1const RatingComponent = ({ maxRating, size, fillColor }) => { 2 // Component logic 3}; 4
We can set default props to ensure our component has sensible defaults:
1RatingComponent.defaultProps = { 2 maxRating: 5, 3 size: '24px', 4 fillColor: 'gold', 5}; 6
Accessibility is crucial for all users to interact with your component. We'll add ARIA attributes and ensure our component is navigable via the keyboard:
1<span 2 role="radio" 3 aria-checked={index < currentRating ? 'true' : 'false'} 4 tabIndex={0} 5 onKeyPress={(event) => { 6 if (event.key === 'Enter') { 7 handleRating(index + 1); 8 } 9 }} 10> 11 ☆ 12</span> 13
By setting the role to "radio" and using aria-checked, we inform screen readers about the state of each star. The tabIndex attribute allows users to navigate through the stars using the tab key, and the onKeyPress event enables rating selection with the 'Enter' key.
Visual feedback is essential for interactivity. We'll add CSS to change the appearance of stars on hover and manage hover states in our component:
1.star { 2 color: grey; 3 cursor: pointer; 4} 5 6.star.filled { 7 color: gold; 8} 9 10.star:hover, 11.star:hover ~ .star { 12 color: lightgrey; 13} 14
In our component, we'll handle the mouse events to update the hover state:
1const [hoverRating, setHoverRating] = useState(0); 2 3// Inside the map function 4<span 5 onMouseEnter={() => setHoverRating(index + 1)} 6 onMouseLeave={() => setHoverRating(0)} 7 className={`star ${index < (hoverRating || currentRating) ? 'filled' : ''}`} 8> 9 ☆ 10</span> 11
Sometimes, you can display a rating that users cannot change. We'll add a readOnly prop to handle this scenario:
1const RatingComponent = ({ readOnly, ...props }) => { 2 // Component logic 3 return ( 4 <div className="rating-container"> 5 {stars.map((_, index) => ( 6 <span 7 key={index} 8 className={`star ${index < currentRating ? 'filled' : ''}`} 9 onClick={!readOnly ? () => handleRating(index + 1) : undefined} 10 > 11 ☆ 12 </span> 13 ))} 14 </div> 15 ); 16}; 17
This conditional onClick ensures that the rating can only be set if the component is not read-only.
To allow for more granular ratings, we'll implement half-star functionality. We'll adjust our logic to support this and consider the precision prop:
1const handleRating = (ratingValue) => { 2 setCurrentRating(precision === 'half' ? Math.ceil(ratingValue * 2) / 2 : ratingValue); 3}; 4
This code snippet rounds the rating to the nearest half when the precision prop is set to "half".
For further customization, we'll allow users to pass their icons and labels for each rating item:
1const RatingComponent = ({ customIcon, labels, ...props }) => { 2 // Component logic 3 return ( 4 <div className="rating-container"> 5 {stars.map((_, index) => ( 6 <span 7 key={index} 8 aria-label={labels ? labels[index] : undefined} 9 className={`star ${index < currentRating ? 'filled' : ''}`} 10 // ... 11 > 12 {customIcon || '☆'} 13 </span> 14 ))} 15 </div> 16 ); 17}; 18
By checking for customIcon and labels props, we provide the ability to define custom icons and accessible labels for each star.
Let's see how we can integrate our rating component into a product review form within a React app:
1import React, { useState } from 'react'; 2import RatingComponent from './RatingComponent'; 3 4const ProductReviewForm = () => { 5 const [rating, setRating] = useState(0); 6 7 const handleSubmit = (event) => { 8 event.preventDefault(); 9 // Submit the review with the rating 10 }; 11 12 return ( 13 <form onSubmit={handleSubmit}> 14 <RatingComponent currentRating={rating} onRatingChange={setRating} /> 15 {/* Additional form fields */} 16 <button type="submit">Submit Review</button> 17 </form> 18 ); 19}; 20 21export default ProductReviewForm; 22
In this example, we're lifting the rating state to the form level, which allows us to submit the rating along with the rest of the review.
We'll write unit tests using a library like Jest or React Testing Library to ensure our rating component works as expected. We'll test that the component renders the correct number of stars, responds to user clicks, and respects the readOnly prop.
Performance optimization is key, especially for components used frequently throughout an application. Here are some tips for optimizing your React rating component:
For developers looking to take their React rating component to the next level, consider exploring CSS modules or styled components for scoped and more powerful styling options. This allows for even greater customization without the risk of style conflicts:
1import styles from './RatingComponent.module.css'; // If using CSS modules 2 3// Inside the map function 4<span className={`${styles.star} ${index < currentRating ? styles.filled : ''}`}> 5 ☆ 6</span> 7
Or with styled-components:
1import styled from 'styled-components'; 2 3const Star = styled.span` 4 color: ${props => props.filled ? 'gold' : 'grey'}; 5 cursor: pointer; 6`; 7 8// Inside the map function 9<Star filled={index < currentRating}> 10 ☆ 11</Star> 12
Good documentation is essential for any reusable component. Document your React rating component's props, expected prop types, default values, and other important information. This will make it easier for other developers to use and contribute to your component.
To illustrate the versatility of the React rating component, consider showcasing real-world examples where it enhances user experience. For instance, a product review section, a survey form, or a user preference setting in a profile page.
We've covered the essentials of building an interactive and accessible React rating component. From handling user interactions to ensuring accessibility and allowing for extensive customization, this component is a valuable addition to any developer's toolkit.
Now, it's time to put this knowledge into practice. Integrate the rating component into your projects, experiment with different styles and behaviors, and see how it can improve the overall user experience.
For those eager to learn more, explore additional resources such as React documentation, community forums, and open-source projects. Contributions to the React community, whether through sharing code snippets or writing tutorials, are always welcome and help foster a collaborative environment.
By following these guidelines and leveraging React's robust ecosystem, you can create a React rating component that meets and exceeds user expectations. Happy coding!
Tired of manually designing screens, coding on weekends, and technical debt? Let DhiWise handle it for you!
You can build an e-commerce store, healthcare app, portfolio, blogging website, social media or admin panel right away. Use our library of 40+ pre-built free templates to create your first application using DhiWise.