Skip to content

Instantly share code, notes, and snippets.

@javascripter
Created November 27, 2024 20:16
Show Gist options
  • Save javascripter/5eac8ccd92edb1082dcf20d578c7ed74 to your computer and use it in GitHub Desktop.
Save javascripter/5eac8ccd92edb1082dcf20d578c7ed74 to your computer and use it in GitHub Desktop.
A hand-optimized React Ratings Component that doesn't render multiple SVGs. Supports fractional values.
import * as React from 'react'
export function Ratings({
value,
width = 18,
height = 16,
gap = 2,
maxStars = 5,
color = '#f39c12',
}: {
value: number
width?: number
height?: number
maxStars?: number
gap?: number
color?: string
}) {
const id = React.useId()
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={width * maxStars + gap * (maxStars - 1)}
height={height}
>
<defs>
<symbol id={id} viewBox="0 0 18 16">
<path d="m16.5 5.4-4.4-0.6-1.9-4c-0.3-0.7-1.4-0.7-1.7 0l-2 4-4.4 .6c-0.8 .1-1.1 1.1-0.5 1.6l3.2 3.1-0.7 4.4c-0.1 .8 .7 1.4 1.4 1l3.8-2.1 3.9 2.1c0.7 .4 1.5-0.2 1.4-1l-0.7-4.4 3.2-3.1c0.6-0.6 .3-1.5-0.5-1.6z" />
</symbol>
</defs>
{Array.from({ length: maxStars }, (_, i) =>
Math.max(0, Math.min(1, value - i)),
).map((star, index) => {
const x = index * (width + gap)
const factor = 0.8
return (
<React.Fragment key={index}>
<use // outer star
href={`#${id}`}
x={x}
y={0}
width={width}
height={height}
fill={star === 1 ? color : 'white'}
stroke={color}
strokeWidth={2}
strokeLinejoin="round"
/>
{star > 0 && star < 1 && (
<use // inner fractional star
href={`#${id}`}
x={x}
y={0}
width={width}
height={height}
fill={color}
stroke={'none'}
strokeWidth={2}
strokeLinejoin="round"
clipPath={`rect(0 ${Math.round(((star - 0.5) * factor + 0.5) * 100)}% 100% 0)`}
/>
)}
</React.Fragment>
)
})}
</svg>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment