Simplicity and Re-usability: React SVG and SVG Props

Scalable Vector Graphics are reliable and high quality graphics which are often used as icons. Unlike JPGs and PNGs, SVGs won’t lose quality in any screen resolution and at any zoom level. We can use these SVGs to reduce our build size exponentially by not compromising on Icon quality. Also we can make it more dynamic using props in React.

React’s React.SVGProps<SVGSVGELElement> is a utility type that automatically provides all valid attributes to an SVG element like height, width, onClick, etc. We can further extend it with our own additional custom attributes to accommodate our needs.

Here is a small example of customizability in action using the following Medal Component created using SVG element with two custom attributes(medal and ribbon).

interface MedalProps extends React.SVGProps<SVGSVGElement> {
  // custom attributes
  medal: {
    dark: string,
    light: string
  },
  ribbon: {
    main: string;
    border: string;
    bottom: string;
  }
};

export const Medal: React.FC<MedalProps> = ({
  medal, 
  ribbon,
  ...props 
  }) => {
  return (
    <svg  width="100%" height="100%" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" {...props}>
      <g id="Flat">
        <g id="Color">d
          <polygon fill={ribbon.border} points="45 17 32 25 19 17 19 3 45 3 45 17" />
          <polygon fill={ribbon.main} points="40 3 40 20.08 32 25 24 20.08 24 3 40 3" />
          <path d="M32,25l6.49-4a21.36,21.36,0,0,0-13,0Z" fill={ribbon.bottom} />
          <circle cx="32" cy="41.5" fill={medal.light} r="19.5" />
          <circle cx="32" cy="41.5" fill={medal.dark} r="14.5" />
          <path d="M33.37,34l1.52,2.63a1.54,1.54,0,0,0,1.06.76L39,38a1.53,1.53,0,0,1,.85,2.56l-2.1,2.22a1.5,1.5,0,0,0-.4,1.22l.36,3a1.57,1.57,0,0,1-2.22,1.58l-2.81-1.27a1.6,1.6,0,0,0-1.32,0l-2.81,1.27A1.57,1.57,0,0,1,26.31,47l.36-3a1.5,1.5,0,0,0-.4-1.22l-2.1-2.22A1.53,1.53,0,0,1,25,38l3-.59a1.54,1.54,0,0,0,1.06-.76L30.63,34A1.59,1.59,0,0,1,33.37,34Z" fill={medal.light} />
        </g>
      </g>
    </svg>
  )
}

Let’s use this component in our App.tsx with two states, one for the medal and the other for ribbon.

import { useState } from 'react'
import './App.css'
import { Medal } from './components/icons'


const medalColors = [
  { light: '#fccd1d', dark: '#f9a215' }, // Gold
  { light: '#c0c0c0', dark: '#a9a9a9' }, // Silver
  { light: '#cd7f32', dark: '#8c5a2e' }, // Bronze
  { light: '#e5e5e5', dark: '#bfbfbf' }, // Platinum
  { light: '#b76e79', dark: '#9b4d58' }, // Rose Gold
  { light: '#7c7c7c', dark: '#595959' }, // Gunmetal
]

const ribbonColors = [
  { main: '#dd051d', border: '#212529', bottom: '#a60416' }, // Red Variant
  { main: '#0056b3', border: '#002952', bottom: '#00428a' }, // Blue Variant
  { main: '#28a745', border: '#155724', bottom: '#1e7e34' }, // Green Variant
  { main: '#6f42c1', border: '#452980', bottom: '#563d7c' }, // Purple Variant
  { main: '#ffc107', border: '#856404', bottom: '#ffca2c' }, // Yellow Variant
  { main: '#e83e8c', border: '#822659', bottom: '#d63384' }, // Pink Variant
  { main: '#17a2b8', border: '#0c5460', bottom: '#138496' }, // Teal Variant
  { main: '#fd7e14', border: '#c8560e', bottom: '#e86d23' }, // Orange Variant
  { main: '#6610f2', border: '#4b0082', bottom: '#5200e7' }, // Indigo Variant
  { main: '#343a40', border: '#1d2124', bottom: '#495057' }, // Dark Gray Variant
]

function App() {

  const [selectedMedalColors, setSelectedMedalColors] = useState(() => medalColors[0])
  const [selectedRibbonColors, setSelectedRibbonColors] = useState(() => ribbonColors[0])

  return (
    <div className='w-full h-screen flex items-center justify-center px-5 sm:px-10'>
      <div className='w-full max-w-7xl grid grid-cols-1 md:grid-cols-2 border-8 border-[#969b81] bg-[#e1eaaa] p-10'>
        {/* Medal Component */}
        <Medal medal={selectedMedalColors} ribbon={selectedRibbonColors} /> 
        <div className='flex flex-col items-center justify-center gap-10'>
          <div>
            <div className='text-center font-bold mb-2.5'>Medals</div>
            <div className='flex gap-3 flex-wrap items-center justify-center'>
              {medalColors.map(colors => <div
                key={colors.dark}
                className='w-10 h-5 sm:w-20 sm:h-10 cursor-pointer'
                style={{ backgroundColor: colors.dark }}
                onClick={() => setSelectedMedalColors(colors)} />
              )}

            </div>
          </div>
          <div>
            <div className='text-center font-bold mb-2.5'>Ribbons</div>
            <div className='flex gap-3 flex-wrap items-center justify-center'>
              {ribbonColors.map(colorScheme => <div
                key={colorScheme.main}
                className='w-10 h-5 sm:w-20 sm:h-10 cursor-pointer'
                style={{ backgroundColor: colorScheme.main }}
                onClick={() => setSelectedRibbonColors(colorScheme)} />
              )}
            </div>
          </div>
        </div>
      </div>
    </div>
  )
}

export default App

medalColors and ribbonColors are self explanatory, these are variants of medals and ribbons respectively. and there are color palettes to change the colors and try variations of medals.

Now, we have 60 variations of medals with 1 SVG component and 2 variables(medalColors and ribbonColors). This will drastically reduces the bundle size of a build and makes the icons dynamic.

Here are some examples of medal varients…

You can find code for this small project to see the other variants in https://github.com/AhmadBShaik/medal-maker.

Thanks for reading this article!

0
Subscribe to my newsletter

Read articles from Ahmad Basha Shaik directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ahmad Basha Shaik
Ahmad Basha Shaik

Ahmad is a dynamic software developer with experience in the tech industry, where he honed his skills and expertise in crafting efficient and innovative solutions. His journey began at a startup, where he thrived in the fast-paced environment, tackling challenging projects and contributing to the company's growth.