Skip to main content

Command Palette

Search for a command to run...

Playing with SVGs

Advanced Customization and Gradient Hacks in React

Updated
4 min read

SVG is one of my favourite formats for images in apps. It’s clean, scalable, and customisable

Need the same icon to work on both light and dark backgrounds? We usually change the color in iconsets. But what about site logos or custom icons downloaded from Figma? Do we make the user download multiple files just to handle a complex gradient change?

Picture showcasing individual SVGs used for each color

Instead, we should identify the hard-coded values in the SVG. If you read the SVG code, you’ll notice that the colour often comes from a block of linearGradient code. Specifically, the stop-color attribute. This might vary for other SVGs, so look out for fill and stroke parameters. They may contain direct colours or point to another element using the url function. If a url is used, locate the <defs> element with the corresponding ID (Image shows path pointing to <defs> with ID “g1“

Lookout for fill and stroke parameters. They may contain direct colors or point to another element using the url function. If url is given, ID find the <defs> element with the required ID

Strategy 1: The Custom Component (CSS Variables)

How do we do this with a single SVG file in React?

  1. First, save the SVG as a component rather than importing it as an image.

  2. You need to replace the static ID (like "g1") with a unique, dynamic ID. This is crucial because we are referencing a DOM element in the fill by its ID. It will always take the value of the first occurrence in the DOM, so if different instances need different colours, they require a unique ID. Also, don’t miss the hash (#) symbol when writing the url access string.

  3. Finally, assign CSS variables to the stopColor attribute. I prefer adding a default fallback value to the variables for safety, like var(--color, "#455673").

You can now use a single SVG component for multiple variations.


Strategy 2: The "External Defs" Trick (For Icon Sets)

What about icon sets like Hugeicons? Often, gradient features are locked behind "Pro" tiers, or the library simply doesn't support them.

We can bypass this by defining a gradient in a separate, invisible SVG and referencing it in the icon's stroke.

Step 1: Create a Gradient Definition Component

This component renders an invisible SVG (height=0, width=0) that serves purely as a container for your gradient definition.

const SVGDefinition = ({ id, className }) => {
  return (
    <svg 
      className="absolute inline h-0 w-0" 
      aria-hidden="true" // Good for accessibility since this is invisible
    >
      <defs>
        <linearGradient
          id={id}
          x1="0" y1="0" x2="0" y2="100%" // gradient
          gradientUnits="userSpaceOnUse"
          className={className} // If using Tailwind classes for colors
        >
          {/* Use stopColor (camelCase) */}
          <stop offset="0%" stopColor="var(--gradient-from)" />
          <stop offset="100%" stopColor="var(--gradient-to)" />
        </linearGradient>
      </defs>
    </svg>
  );
};

Step 2: Apply it to the Icon

Place the definition alongside your icon, then override the icon's stroke style to point to your new gradient ID.

const Component = () => {
  const gradientId = 'unique_gradient_id'; // Descriptive variable name

  return (
    <span className="relative">
       <SVGDefinition id={gradientId} />
       <IconFromIconset 
         style={{
           // React inline styles require quotes and comma separation
           stroke: `url(#${gradientId}) !important`, 
         }}
       />
    </span>
  );
}

Why this works

The url(#id) syntax in SVG doesn't care where the ID live. It just looks for a matching ID anywhere in the DOM. By injecting our own <defs> nearby, we can "paint" the third-party icon with our custom gradient.

Notes:

  1. The defined gradient needs to be position absolute with width:0 and height:0. This ensures it doesn’t take up any physical space on your page.

  2. If this gradient is a primary brand colour used everywhere, you can simply add this definition to the top of your body tag to reduce overhead. You wouldn't need a wrapper in that case, just use the icon with the adjusted stroke value.

I have tested this approach on size, stroke widths, fill, and filter attributes. Let me know if you find something intersting or have any feedback :D

References:

  1. https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/defs#/

  2. https://developer.mozilla.org/ru/docs/Web/SVG/Reference/Element/linearGradient#/

  3. https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/stop-color#/

Previously published on Medium