The Mystery of the Vanishing Icon
You export an icon from Figma. It has linear gradients and clip paths. You convert it to a React component, render it, and it looks perfect.
Then you reuse that component elsewhere on the page, or render a list of cards that each contain it. The gradients vanish. Icons turn solid black or disappear entirely.
This bug traces back to how Figma generates vector assets.
Global DOM ID Clashes
Figma exports self-contained SVG assets. A gradient gets an internal <defs> block with a <linearGradient> element carrying a fixed ID like id="paint0_linear_1_5". The path references it with fill="url(#paint0_linear_1_5)".
The problem starts when this asset enters the shared DOM of a web page.
HTML specifications strictly require that all DOM IDs be document-wide unique. When you render multiple instances of the same React component, the browser sees duplicate IDs across your document.
<!-- The resulting DOM structure -->
<svg>
<defs>
<linearGradient id="gradient-1">...</linearGradient>
</defs>
<rect fill="url(#gradient-1)">
</svg>
<svg>
<defs>
<!-- Duplicate ID! -->
<linearGradient id="gradient-1">...</linearGradient>
</defs>
<!-- Resolves incorrectly to the FIRST gradient-1 in the DOM! -->
<rect fill="url(#gradient-1)">
</svg>
When multiple instances of an SVG exist on a page, the browser resolves url(#id) references to the first matching ID it finds in the DOM tree.
The Invisible Fill Trap
All instances reference the <defs> of the first mounted instance. If that first instance gets hidden (display: none in a mobile menu) or unmounted by React, the gradient definition disappears from the DOM. Every other instance loses its fill and falls back to solid black or transparent.
The Fix: React's useId
Your SVG components should generate instance-specific IDs at runtime. React 18 introduced the useId hook for this exact problem, and React 19 refined it further.
Each component render gets its own unique hash, so gradients and clip paths never collide no matter how many instances exist:
import React, { useId, SVGProps } from 'react';
export const GradientCardIcon = (props: SVGProps<SVGSVGElement>) => {
const uniqueId = useId();
// Generate instance-specific IDs
const gradientId = `card-gradient-${uniqueId}`;
const clipPathId = `card-clip-${uniqueId}`;
return (
<svg viewBox="0 0 100 100" {...props}>
<defs>
<linearGradient id={gradientId} x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stopColor="#4F46E5" />
<stop offset="100%" stopColor="#EC4899" />
</linearGradient>
<clipPath id={clipPathId}>
<rect width="100" height="100" rx="12" />
</clipPath>
</defs>
{/* Reference the instance-specific IDs */}
<g clipPath={`url(#${clipPathId})`}>
<rect width="100" height="100" fill={`url(#${gradientId})`} />
</g>
</svg>
);
};
useId fixes the collision, but it means 100 instances create 100 duplicate <linearGradient> definitions in the DOM.
For performance-critical cases, a "DefsPortal" pattern can help. You use React Context and Portals to hoist all SVG <defs> into a single centralized <svg> block at the layout root, deduplicating gradient definitions while keeping individual components lightweight.
Automating the Fix
Manually wiring useId into every Figma export is tedious. A client-side SVG to JSX Converter can detect internal URI references (gradients, masks, clip paths) during conversion and inject dynamic useId bindings into the output component, making it collision-safe from the start.