Create a Custom NextJS Star Rating Component From Scratch
Add to your RSS feed14 December 20234 min readTable of Contents
Sure, creating a custom Star Rating component in Next.js involves creating a reusable React component that allows users to rate something using stars. Let's create a simple Star Rating component from scratch. We'll use React state to manage the rating and handle user interactions.
Prerequisites:
Before we begin, ensure that you have Node.js and npm (Node Package Manager) installed on your system. Additionally, you should have NestJS CLI installed globally. You can install it with the following command:
npm install -g @nestjs/cliInstallation
1. Setting Up the Project:
nest new rating2. Add TailwindCSS:
npm install -D tailwindcssInitialize the tailwind.
npx tailwindcss init -p3. Configure your tailwind.config.js file
1 /** @type {import('tailwindcss').Config} */2 module.exports = {3 content: [4 './app/**/*.{js,ts,jsx,tsx,mdx}',5 './pages/**/*.{js,ts,jsx,tsx,mdx}',6 './components/**/*.{js,ts,jsx,tsx,mdx}',78 // Or if using `src` directory:9 './src/**/*.{js,ts,jsx,tsx,mdx}',10 ],11 theme: {12 extend: {},13 },14 plugins: [],15 };
4. Add Tailwind layers to the globals.css
1 @tailwind base;2 @tailwind components;3 @tailwind utilities;
5. Create a new folder named Rating in the components folder:
6. Create 5 new files for Rating and Star Components:
Rating.tsx
Rating.props.ts
Rating.module.css
Star.tsx
Star.props.ts
7. Let's begin with the Star Component:
Modify the Start.props.ts file:
1 import { DetailedHTMLProps } from 'react';23 export interface StarProps4 extends DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> {5 isFilled: boolean;6 }
Here we are extending the DetailedHTMLProps to provide the all necessary props as onClick, onMouseEnter, onMouseLeave, TabIndex, and other.
Modify Star.tsx
1 /* eslint-disable max-len */2 'use client';34 import { StarProps } from './Star.props';56 const Star = ({ isFilled = false, className, ...props }: StarProps) => {7 return (8 <span className={className}>9 <svg10 width='20'11 height='20'12 viewBox='0 0 20 20'13 fill={isFilled ? '#FD7E14' : '#212121'}14 xmlns='http://www.w3.org/2000/svg'15 {...props}16 >17 <path18 fillRule='evenodd'19 clipRule='evenodd'20 d='M13.4713 17.739C13.649 17.8405 13.8521 17.9167 14.0807 17.9167C14.4107 17.9167 14.7662 17.7643 14.9693 17.485C15.1725 17.2057 15.2486 16.8503 15.1725 16.5202L14.1314 12.0768C14.1314 12.0261 14.1568 11.9753 14.1822 11.9499L17.5084 8.9538C17.8639 8.64911 17.9908 8.16669 17.8385 7.73505C17.6861 7.30341 17.3053 6.99872 16.8482 6.97333L12.5572 6.64325C12.5064 6.64325 12.481 6.61786 12.4557 6.56708L10.856 2.40302C10.6783 1.94598 10.2721 1.66669 9.78964 1.66669C9.30722 1.66669 8.90097 1.94598 8.69785 2.40302L7.09824 6.56708C7.07285 6.61786 7.04746 6.64325 6.99667 6.64325L2.70566 6.97333C2.24863 6.99872 1.86777 7.30341 1.71542 7.73505C1.58847 8.16669 1.71542 8.64911 2.0455 8.9538L5.37167 11.9499C5.42246 11.9753 5.42246 12.0261 5.42246 12.0768L4.40683 16.5202C4.30527 16.8503 4.38144 17.2057 4.60996 17.485C4.81308 17.7643 5.14316 17.9167 5.49863 17.9167C5.72714 17.9167 5.93027 17.8405 6.13339 17.739L9.73886 15.3776C9.78964 15.3522 9.81503 15.3522 9.86582 15.3776L13.4713 17.739Z'21 fill={isFilled ? '#FD7E14' : 'none'}22 stroke={isFilled ? '#FD7E14' : '#212121'}23 />24 </svg>25 </span>26 );27 };28 export default Star;
8. Next step: modify the Rating.props.ts file:
1 import { DetailedHTMLProps } from 'react';23 export interface StarProps4 extends DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> {5 isFilled: boolean;6 }
9. New let's create the Rating.tsx file:
Rating.module.css
1 .rating {2 @apply flex gap-2;3 }4 .filled {5 @apply fill-yellow;6 }
Rating.props.ts
1 import { DetailedHTMLProps } from 'react';23 export interface RatingProps4 extends DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> {5 isEditable: boolean;6 rating: number;7 setRating?: (rating: number) => void;8 }
Rating.tsx
1 import { useEffect, useState, KeyboardEvent } from 'react';23 import { RatingProps } from './Rating.props';4 import styles from './Rating.module.css';5 import Star from './Star';67 const Rating = ({8 isEditable = false,9 rating,10 setRating,11 className,12 ...props13 }: RatingProps): JSX.Element => {14 const [ratingArray, setRatingArray] = useState<JSX.Element[]>(new Array(5).fill(<></>));1516 const hoverHandle = (idx: number) => {17 if (!isEditable) {18 return;19 }20 constructRating(idx);21 };22 const clickHandle = (idx: number) => {23 if (!isEditable || !setRating) {24 return;25 }26 setRating(idx);27 };2829 const handleKeyDown = (e: KeyboardEvent<HTMLSpanElement>, idx: number) => {30 if (!isEditable || !setRating) {31 return;32 }33 if (e.code !== 'Space') {34 return;35 }36 setRating(idx);37 };3839 const constructRating = (currentRating: number) => {40 console.log('currentRating', currentRating);41 const updatedArray = ratingArray.map((ratingItem: JSX.Element, idx: number) => {42 return (43 <Star44 isFilled={idx < currentRating}45 key={idx}46 onMouseEnter={() => hoverHandle(idx + 1)}47 onMouseLeave={() => hoverHandle(rating)}48 onClick={() => clickHandle(idx + 1)}49 className={`${className || ''} ${isEditable ? 'cursor-pointer' : ''}`}50 tabIndex={isEditable ? 0 : -1}51 onKeyDown={(e: KeyboardEvent<HTMLSpanElement>) => handleKeyDown(e, idx + 1)}52 />53 );54 });55 setRatingArray(updatedArray);56 };5758 useEffect(() => {59 constructRating(rating);60 }, [rating]);6162 return (63 <div className={styles.rating} {...props}>64 {ratingArray.map((r: JSX.Element, idx) => (65 <span key={idx}>{r}</span>66 ))}67 </div>68 );69 };70 export default Rating;
10. Use the Rating component in your app router. Open app/page.tsx and import and use the component:
We need also to provide the setRating function to the component.
1 'use client';2 import { useState } from 'react';34 import Rating from '@/components/ui/Rating/Rating';56 export default function Home(): JSX.Element {7 const [rating, setRating] = useState(3);8 return (9 <main>10 <Rating isEditable={true} setRating={setRating} rating={rating} />11 <Rating isEditable={false} rating={4} />12 </main>13 );14 }
11. Run your Next.js app:
npm run devVisit http://localhost:3000 in your browser, and you should see your custom Star Rating component.
This example is a simple representation of a star rating component. You can customize it further by adding styles, animations, and additional features based on your requirements.