BlogSVG customization (SVGR)
SVG customization (SVGR)
Next.js
React
SVG
Front-end
Language : English
by Rayane Merlin
30/10/2025
Hello there ! Today, we're gonna explore how I was able to customize SVGs on my web project WorldMaster. We're gonna see in the first step the made customization applied to SVGs to make you better understand the process.
Before you begin 📝
Prerequisites
What is SVGR ?
SVGR is a tool that transforms SVGs into React components. This allows us to easily manipulate and customize SVGs using React's props and state. In our case, this library was included in the next.js configuration to permit to keep control over all SVGs parts. Initially, importing an SVG in React would treat it as a static file as the source of
<img/>
SVGR is a tool that transforms SVGs into React components. This allows us to easily manipulate and customize SVGs using React's props and state.
There's a lot of options to configure SVGR to fit your needs. In our case, we used the default with some modifications in the next.config.mjs file to allow importing SVGs as React components.
/** @type {import('next').NextConfig} */
const nextConfig = {
...
webpack(config) {
// Grab the existing rule that handles SVG imports
const fileLoaderRule = config.module.rules.find((rule) =>
rule.test?.test?.('.svg'),
)
fileLoaderRule.exclude = /\.svg$/i;
config.module.rules.push(
{
test: /\.svg$/i,
use: [{
loader: '@svgr/webpack',
options: {
svgo: true,
svgoConfig: {
plugins: [
/* Initially, we had these options to clean up the SVGs but for our
customization needs, we disabled them. Each colorable part needed to keep
its id and className without any prefix. We will detail this later.*/
{ name: 'prefixIds',
params: { prefixIds: false, prefixClassNames: false, prefix: false } },
{ name: 'cleanupIds', params: { remove: false, minify: false, prefix: '' } },
]
},
},
}],
},
);
return config;
},
};Reminder
- SVG stands for Scalable Vector Graphics, which is an XML-based vector image format for two-dimensional graphics with support for interactivity and animation.
- SVG are composed of various XML elements such as ,
<path>,<circle>,<rect>, and more, which define the shapes and lines that make up the image. The sub elements represent different parts of the SVG graphic.<line>
All SVGs used were taken from an open source repository on Github called flag-icons. Initially without any interactivity.
Right now, let's see how we passed from a static SVG to a customized one, clickable and listing its functionalities.
Clickable SVGs & Shapes Background Image
Here is an example of a clickable SVG flag. On the right, the original SVG without any customization. And on the left the final customized and interactive SVG.

⚠️ Important to know :
- All SVGs used in this project are imported as React Components, which allows us to manipulate them easily.
- To make an SVG clickable, we can simply add an event handler to the part we want to be interactive. For this, a simple querySelector from the parent component is enough to get all the desired parts.
onClick()
import styles from '.../style.module.scss';
// { ... React component code ... }
const initShapes = () => {
const acceptedTags: string[] = ['path', 'circle', 'g', 'rect'];
const allShapes: NodeListOf<SVGElement> = svgContainer.current?
.querySelectorAll(acceptedTags.map((selector: string) =>
(`svg:not(.${logoStyles.appLogoSvg}) ${selector}[fill]:not(defs *)`)).join(', '));
const colorableShapesTmp: Shape[] = [];
let flagColors: string[] = [];
allShapes.forEach((shape: SVGElement) => {
if ((shape.classList.contains("keep"))) {
const initialFill: string | null = shape.getAttribute('fill');
if (initialFill !== null && initialFill !== 'none' && !initialFill.startsWith("url")) {
const rgbInitialFill: string = hexToRgb(initialFill);
shape.setAttribute('fill', 'url(#pngPattern)');
shape.setAttribute('data-fill', rgbInitialFill);
shape.classList.add(styles.svgContent);
colorableShapesTmp.push(shape);
if (!flagColors.some((color) => (rgbInitialFill === color))) {
flagColors.push(rgbInitialFill);
}
}
}
});
};In this code snippet, we select all the SVG elements that can be colored (like
<path><circle>filldata-fillfill<defs>url(#pngPattern)<defs>There's the component used to define the SVG pattern with the background image. We set the image source to a PNG file located in the public folder.
interface SvgPatternInterface
{ x: number, y: number, patternWidth: number, patternHeight: number }
const SvgPattern = ({ x, y, patternWidth, patternHeight }: SvgPatternInterface) => (
<defs>
<pattern xmlns="http://www.w3.org/2000/svg" id="pngPattern" patternUnits="userSpaceOnUse" width={ patternWidth } height={ patternHeight }>
<image xmlnsXlink="http://www.w3.org/1999/xlink"
{/* Set the initial background with the png (public/) */}
xlinkHref={"/images/patterns/png-background.jpg" }
x={ x } y={ y } width={ imageWidth } height={ imageHeight }
/>
</pattern>
</defs>
);This
<SvgPattern/>/images/patterns/png-background.jpgpatternWidthpatternHeightAdding this pattern after rendered directly in the SVG component permit to have all shapes filled with the background image. And this is why we need to handle SVGs as React components, to be able to manipulate their structure and attributes.
Here's how we insert the
<defs>const SvgPattern: ReactNode = (<SvgPattern
pattern={pattern}
/>);
const svgPatternString: string = renderToString(SvgPattern);
colorableSvgElement.insertAdjacentHTML('afterbegin', svgPatternString);The rendered SVG now has a pattern defined in its
<defs><svg ... >
<defs>
<pattern id="pngPattern" patternUnits="userSpaceOnUse" width="800" height="600">
<image xlink:href="/images/patterns/png-background.jpg" x="0" y="0" width="1600" height="1200"/>
</pattern>
</defs>
<path fill="url(#pngPattern)" data-fill="rgb(255, 0, 0)" ... />
<circle fill="url(#pngPattern)" data-fill="rgb(0, 0, 255)" ... />
</svg>Finally, we can add interactivity to the SVG by attaching
onClickdata-fillconst handleShapeClick = (event: MouseEvent<SVGElement>) => {
const target = event.currentTarget;
const originalFill = target.getAttribute('data-fill');
if (!originalFill) throw new Error("No original fill found");
target.setAttribute('fill', originalFill);
};SVG files initialization
To make all this work, we had to modify the original SVG files a bit. Since we wanted to allow users to click on different parts of the SVG and see their original colors, we needed a way to identify which parts should be interactive. Taking the flag of Mexico as an example, we added a specific class "keep" to all the SVG elements that we wanted to be clickable and have their colors changeable. This is why we removed the svg class prefixes in the NextJS Config file. This class acts as a marker for our JavaScript code to identify which parts of the SVG should be processed. The problem with this method is that it requires manual modification of each SVG file to add the "keep" class to the desired elements. This can be time-consuming, especially if you have a large number of SVGs to process. A simple solution for this was to do it manually with creating a new route in the project to display all SVGs and edit them directly, allowing to see the changes in real-time (svg structure hot reloading).

After selecting all colorable parts for each flag, we obtained SVGs with the necessary classes added. This allowed our JavaScript code to easily identify and manipulate the correct parts of the SVGs during runtime.
There's an example of the Mexico flag SVG before and after adding the classes to the colorable shapes. The middle part containing the eagle was initially selectable (left), and left unchanged as it doesn't need to be interactive (right).

After that, our JavaScript code can simply look for elements with the "keep" class and apply the necessary logic to make them interactive and customizable.
Final Thoughts & Recap
In this blog post, we explored how to customize SVGs in a React/Next.js project using SVGR, from its configuration to its usage. We learned how to import SVGs as React components, manipulate their structure, and add interactivity by attaching event handlers to specific parts of the SVG.
We also discussed the importance of modifying the original SVG files to add specific classes to the elements we wanted to be interactive. This allowed us to easily identify and manipulate those parts during runtime.
By following the steps outlined in this post, you can create dynamic and interactive SVGs that enhance the user experience on your web projects. Whether you're building a flag selector, an icon library, or any other application that involves SVG graphics, the techniques discussed here will help you achieve your goals. There's an example in NextJS for the final result of Mexico Colorable Flag on WorldMaster, but feel free to experiment and adapt these techniques to your own projects!
Thank you for reading ! 🚀
© This post is licensed under the CC BY 4.0 license.