Handle SVGs in React properly
June 23, 2025
We should talk about SVGs in your JavaScript / React apps. Long story short: putting them there bloats your bundle and degrades the performance of your app. Let’s take a look at a better approach for handling SVG, while keeping our JS bundle small and the page performant.
TLDR
SVG sprites give you the best performance-to-DX tradeoff.
Why you shouldn't Image + SVG
Using an SVG as an image tag seems fine at first. The browser grabs the image from your public
folder (or wherever), and it just works. The upside is that these external assets get cached by the browser or your CDN, so they load faster the next time you visit this page. But here’s the problem: there’s usually a little flicker when the image loads, because the browser first downloads your HTML document, then starts downloading images, scripts, and styles. Also, when you use an <img />
tag for your icons, you can't style or animate the SVG with CSS.
Why you shouldn't inline SVG
Another approach is often used to combat the issues I've just mentioned - inlining the SVG into the HTML document. When the Browser downloads the HTML, it doesn’t need to make a secondary request for the image asset; it is there immediately (no flicker). The other benefit is that the icon can be styled with CSS. Although it looks like a solution, this approach is not without pitfalls. Inlining the SVG into your HTML reduces memory performance because you add many elements to the page. There is a nice article on the topic by Chrome engineers.
Another problem is that the SVG bloats your JavaScript bundle size. The browser has to download, parse, and evaluate the JavaScript on the page before anything renders. Including SVG in your bundle makes you pay this cost twice. You have a larger bundle to download and parse (path data can be large for some icons, especially if they are more intricate), then the rendered HTML adds many of additional elements to the page, slowing down DOM traversal. A lot of React apps use this second technique, but I knew there had to be a better way.
Rendering Icons using SVG Sprites
There’s a better way to handle SVGs that eliminates the problems of both <img>
and inlining: SVG Sprites.
This trick’s been around for a while, and it works surprisingly well. If you’ve done anything with game dev, you’ll get the concept. Instead of having a bunch of individual icons scattered around, you combine them into one file - a sprite sheet. Then you just reference the specific icon you need.
The browser only loads the sprite sheet once, so you avoid the flicker of separate image requests, and you also avoid stuffing a bunch of inline SVGs into your JSX. Best of both worlds.
The Symbol Element
Let me introduce you to the <symbol>
element. The symbol element “is used to define graphical template objects which can be instantiated by a <use>
element.” - MDN. When combined with the <defs>
element, we can construct a SVG sprite with our icons.
First, we create a file sprite.svg, and add an <svg>
element that wraps a defs
element and a <symbol>
. Next, we take the icon (that we would have inlined), swap the SVG for a symbol element, and give it an ID. The id is important! Finally, we’ll add a second icon to the sprite by adding it as a symbol.
You can keep defining all your icons as symbols in this file (although be conscious of the file size, keep it under 125kb). Next, we will create a React component that will reference our sprite.
The Use Element
In the example above, the magic happens in the <use>
element which links to the “Fragment Identifier” (the ID that we defined on the symbol). Now, we have an icon component that lets us do all the things we could do with inline SVGs (like defining the height, width, and color of the icon), but all of the path data lives in an external asset (and not in the JavaScript bundle).
Preloading
You can preload
the sprite file (and then cache it) to improve performance. “By preloading a certain resource, you are telling the browser that you would like to fetch it sooner than the browser would otherwise discover it because you are certain that it is important for the current page.” - web.dev.
To preload the sprite, add a link tag to the head of the document.
Depending on your server’s configuration, you might need to make sure the proper cache-headers are set on your sprite.svg so the browser can cache it appropriately.
Introducing @neodx/svg
So far, the @neodx/svg library is a game-changer for handling SVG Sprites. It gives us a ready-to-go solution with great DX, like type-safety and a guide on how to build your Icon component. Put your icon into the assets folder, and it will create the types and generate the new sprite.
Included features:
- Types and metadata generation for your sprites
- Built-in integration for all major bundlers: Vite, Next.js, Webpack and others with the power of unplugin
- Optional grouping by folders
- Optimization with svgo
- Automatic colors reset
Configuration
I will show the setup process for a Next.js project. Add @neodx/svg/webpack
plugin to next.config.js
and configure it for your needs.
Create your Icon
component:
Read this guide to understand the Icon component we've just built.
Use your Icon
. It will type hint the name of your icons from the sprites.
That’s it! Hope this was helpful.