Using SVGs in React
January 20, 2023
I think it's safe to say that you're behind the times if you're not using SVGs for vector image assets like logos, icons, and decorative elements on your sites. After all, their benefits are plentiful and include:
- easy scaling without any loss of detail
- direct styling via CSS
- simple editing in any text editor (after all, SVGs are just made up of XML)
That said, they're not the easiest things to work with. Specifically, figuring out how to include them on your site can be kind of a pain because there are a bunch of different options.
In this post we'll explore:
- Two common methods for using SVGs on a basic site
- Easily converting SVGs into valid JSX using SVG2JSX
- How to use a React component for SVGs
Methods of Using SVGs
The two easiest and most common methods to include SVGs on a website are:
- using an
img
tag - inlining the SVG code directly in the HTML of the site
Using an img
Tag
You should use an img
tag to include an SVG if: you want your SVGs managed via a CMS like WordPress; or you're writing a basic static site without any kind of templating engine or front-end framework. Using an img
tag lets you keep track of your SVGs in a central location. You can update them at any time without having to worry about finding all the places you have used them throughout your site (that is, it keeps your code DRY
).
<div class="svg-container">
<img src="./path/to/your.svg" />
</div>
Inlining the SVG
However, my favorite way to use SVGs is to simply inline them in my markup. Doing it this way means it's incredibly easy to style and manipulate the SVG. Plus the SVG is embedded directly into the source code for your site. So you don't incur any extra network calls as you do when using an img
tag.
Inlining SVGs directly in HTML is best when you either: only need to use the SVG once; or you are using some sort of templating engine or front-end framework to extract it its own component. If you need to use an SVG in multiple places but can't extract and import your SVG where you need it then you're probably better off using an img
tag.
<div class="svg-container">
<svg>
<!-- SVG code goes here -->
</svg>
</div>
Converting SVG Files to Valid JSX
Let's say we want to turn the following SVG image into a React component:
The source file looks like this:
<?xml version="1.0"?>
<svg fill="#719BEC" fill-opacity="0.5" viewBox="0 0 500 500" width="250" height="250"
xmlns="http://www.w3.org/2000/svg">
<circle cx="250" cy="250" r="210" stroke="#000" strokeWidth="8"></circle>
</svg>
In order to use our source SVG as a React component we have to make sure it's valid JSX. As it stands, our source SVG element is not valid JSX. So we can't just open up the SVG file in a text editor, copy, and paste the SVG element into a .jsx
or .tsx
file.
Most of the problems I have run into trying to do so revolve around simple capitalization: an SVG element is just an HTML element and so generally prefers kebab-case (eg. clip-path
and clip-rule
); meanwhile JSX prefers camelCase (eg. clipPath
and clipRule
). In our source SVG file above we have a fill-opacity
attribute; in JSX this should be fillOpacity
. Additionally, our source SVG has an xml
tag we won't need in our JSX.
These issues are simple enough to correct by hand with our small file. As we work with larger and more complex SVGs, though, the work is tedious and it's easy to miss a case or two.
SVG 2 JSX
Instead of converting SVGs to JSX by hand, I like to use a free tool called SVG 2 JSX. This tool takes in your SVG file and spits out valid JSX. It even wraps it in a functional component for you! All you need to do is copy the output text and plop it into a component file and you're ready to go. (Alternatively, you can just copy the svg
element and plop it wherever you need in your existing JSX.)
Using a React Component
Once we have the icon component generated by SVG 2 JSX all we need to do is plop it into a component file. Once we do that we can use it anywhere we want just like it's a normal component (because it is!)
import React from "react"
function Icon() {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="250"
height="250"
fill="#719BEC"
fillOpacity="0.5"
viewBox="0 0 500 500"
>
<circle cx="250" cy="250" r="210" stroke="#000"></circle>
</svg>
)
}
export default Icon
Improving the Our SVG Component
Our SVG graphic component is easy to use and reuse throughout our app, but it does have some problems as its currently implemented. Chief among them is that we have lost the ability to interact with the SVG HTML element. As you can see, width
and height
are hard-coded into the SVG, meaning it'll always be 250px
by 250px
. This is easily solved though.
To make our component behave more like a wrapper for the SVG element it returns we need to make it behave like a wrapper. All we need to do this is to pass all props given to our wrapper to the SVG HTML element. In TypeScript this looks like:
import React from "react"
// TypeScript: type the props to be the same as a normal SVG element
function Icon(props: React.ComponentProps<"svg">) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="250"
height="250"
fill="#719BEC"
fillOpacity="0.5"
viewBox="0 0 500 500"
// Expand given props last to overwrite existing attributes
// This let's us pass arbitrary values
{...props}
>
<circle cx="250" cy="250" r="210" stroke="#000"></circle>
</svg>
)
}
export default Icon
Now we can use our icon component anywhere and have control over the SVG HTML element it will spit out. For instance, that width
and height
problem I mentioned before won't be a problem anymore:
import Icon from './SVGIcon'
const App = (props) => (
<div>
// These width and height values will overwrite the values in SVGIcon.tsx
<Icon width="500" height="500" />
</div>
)
export default App
We can even use tailwind directly on it, if that's our thing:
import Icon from './SVGIcon'
const App = (props) => (
<div>
<Icon
width="500"
height="500"
className="fill-blue-500"
/>
</div>
)
export default App
Conclusion
This post has explored: common methods for using SVGs on websites, how to easily convert a given SVG file into valid JSX, and how to effectively use that JSX within a reusable wrapper component in React. Be sure to check out SVG 2 JSX. It is one of my favorite tools and saves me quite a bit of time and mental energy. Hopefully it will help you as much as it has me!