I recently went to Michael Jackson and Ryan Florence’s ReactJS Training. I was really excited to attend, partially because I had so many questions about SVG and React. There are a lot of bits about working with React and SVG, and especially manipulating it, that aren’t quite supported yet. One of the major gaps for me was the <use>
element, as most SVG icon systems are built with <use>
.
I asked Michael if he thought better support might be coming for some of these features, but he showed me a much better way of working with it, circumventing this method entirely. We’ll go over this technique so that you can get started writing scalable SVG Icon Systems in React, as well as some tricks I’d propose could work nicely, too.
Note: It’s worth saying that use support was recently improved, but I’ve noticed it’s spotty at best and there are other routing and XML issues. We’ll show you another, cleaner way here.
<use>
?
What is For those not familiar how SVG icon systems are typically built, it works a little like this. The <use>
element clones a copy of any other SVG shape element with the ID you reference in the xlink:href
attribute, and still manipulate it without reiterating all of the path data. You may wonder why one wouldn’t just use an SVG as an <img>
tag. You could, but then every icon would be an individual request and you wouldn’t have access change parts of the SVG, such as the fill
color.
Using <use>
allows us to keep the path data and basic appearance of our icons defined in one place so that they could be updated once and change everywhere, while still giving us the benefit of updating them on the fly.
Joni Trythall has a great article about use and SVG icons, and Chris Coyier wrote another awesome article here on CSS-Tricks as well.
Here’s a small example if you’d like to see what the markup looks like:
See the Pen bc5441283414ae5085f3c19e2fd3f7f2 by Sarah Drasner (@sdras) on CodePen.
Why bother with SVG Icons?
Some of you at this point might be wondering why we would use an SVG icon system rather than an icon font to begin with. We have our own comparison on that subject. Plus there are a ton of people writing and speaking about this right now
Here are some of the more compelling reasons, in my mind:
- Icon fonts are hard to make accessible. SVG has the ability to add title and ARIA tags, which provide a huge boon to accessibility, particularly in cases when the icon is alone and a single source of informative navigation. Think: blind people, dyslexic people, the elderly (You will be elderly too someday, hopefully, so if you’re not the kind of dev to care about this subset, do it for the karma! But seriously, care for the elderly.)
- Icon fonts aren’t as crisp on some displays. You can avoid this by doing some fancy font-smoothing in CSS, but here’s one caveat I’ve noticed: it’s difficult to override without turning font-smoothing off entirely. SVGs are more crisp in general, drawing is what they’re built for.
- Icon fonts fail a good amount of the time. Most developers I know have run into scenarios with missing glyph X in a box, there are a lot of ways that icon fonts can fail where SVGs do not. Be it CORS problems or Opera mini, it’s a headache.
- Icon fonts are difficult to position. They’re an image that you’re positioning with font styles. ‘Nuff said. You can’t animate pieces of them without hacky stacking. SVGs offer a navigable DOM to animate parts of an icon, or colorize sections. Not everyone would want to do this, but it sure is nice to have the option.
If you’re like me and updating an enormous codebase, where in order to move over from an icon font to SVG you’d have to update literally hundreds of instances of markup, I get it. I do. It might not be worth the time in that instance. But if you’re rewriting your views and updating them with React, it’s worth revisiting an opportunity here.
<use>
in React
Tl;dr: You don’t need After Michael patiently listened to me explain how we use <use>
and had me show him an example icon system, his solution was simple: it’s not really necessary.
Consider this: the only reason we were defining icons to then reuse them (usually as <symbol>
s in <defs>
) was so that we didn’t have to repeat ourselves and could just update the SVG paths in one spot. But React already allows for that. We simply create the component:
// Icon
const IconUmbrella = React.createClass({
render() {
return (
<svg className="umbrella" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" aria-labelledby="title">
<title id="title">Umbrella Icon</title>
<path d="M27 14h5c0-1.105-1.119-2-2.5-2s-2.5 0.895-2.5 2v0zM27 14c0-1.105-1.119-2-2.5-2s-2.5 0.895-2.5 2c0-1.105-1.119-2-2.5-2s-2.5 0.895-2.5 2v0 14c0 1.112-0.895 2-2 2-1.112 0-2-0.896-2-2.001v-1.494c0-0.291 0.224-0.505 0.5-0.505 0.268 0 0.5 0.226 0.5 0.505v1.505c0 0.547 0.444 0.991 1 0.991 0.552 0 1-0.451 1-0.991v-14.009c0-1.105-1.119-2-2.5-2s-2.5 0.895-2.5 2c0-1.105-1.119-2-2.5-2s-2.5 0.895-2.5 2c0-1.105-1.119-2-2.5-2s-2.5 0.895-2.5 2c0-5.415 6.671-9.825 15-9.995v-1.506c0-0.283 0.224-0.499 0.5-0.499 0.268 0 0.5 0.224 0.5 0.499v1.506c8.329 0.17 15 4.58 15 9.995h-5z"/>
</svg>
)
}
});
// which makes this reusable component for other views
<IconUmbrella />
See the Pen SVG Icon in React by Sarah Drasner (@sdras) on CodePen.
And we can use it again and again, but unlike the older <use>
way, we don’t have an additional HTTP request.
Two SVG-ish things you might notice from the above example. One, I don’t have this kind of output:
<?xml version="1.0" encoding="utf-8"?>
<!-- Generated by IcoMoon.io -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
Or even this on the SVG tag itself:
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" …
That’s because I’ve made certain to optimize my SVGs with SVGOMG or SVGO before adding the markup everywhere. I strongly suggest you do as well, as you can reduce the size of your SVG by a respectable amount. I usually see percentages around 30% but can go as high as 60% or more.
Another thing you may notice is I’m adding a title and ARIA tag. This is going to help screen readers speak the icon for people who are using assistive technologies.
<svg className="umbrella" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" aria-labelledby="title">
<title id="title">Umbrella Icon</title>
Since this id has to be unique, we can make pass props to our instances of the icon and it will propagate to both the title and aria tag like so:
// App
const App = React.createClass({
render() {
return (
<div>
<div className="switcher">
<IconOffice iconTitle="animatedOffice" />
</div>
<IconOffice iconTitle="orangeBook" bookfill="orange" bookside="#39B39B" bookfront="#76CEBD"/>
<IconOffice iconTitle="biggerOffice" width="200" height="200"/>
</div>
)
}
});
// Icon
const IconOffice = React.createClass({
...
render() {
return (
<svg className="office" xmlns="http://www.w3.org/2000/svg" width={this.props.width} height={this.props.height} viewBox="0 0 188.5 188.5" aria-labelledby={this.props.iconTitle}>
<title id={this.props.iconTitle}>Office With a Lamp</title>
...
</svg>
)
}
});
ReactDOM.render(<App/>, document.querySelector("#main"));
The best part, perhaps
Here’s a really cool part of this whole thing: aside from not needing additional HTTP requests, I can also completely update the shape of the SVG in the future without any need for markup changes, since the component is self-contained. Even better than that, I don’t need to load the entire icon font (or SVG sprite) on every page. With all of the icons componentized, I can use something like webpack to “opt-in” to whatever icons I need for a given view. With the weight of fonts, and particularly heavy icon font glyphs, that’s a huge possibility for a performance boon.
All of that, plus: we can mutate parts of the icon on the fly with color or animation in a very simple way with SVG and props.
Mutating it on the fly
One thing here you might have noticed is we’re not yet adjusting it on the fly, which is part of the reason we’re using SVG in the first place, right? We can declare some default props on the icon and then change them, like so:
// App
const App = React.createClass({
render() {
return (
<div>
<IconOffice />
<IconOffice width="200" height="200"/>
</div>
)
}
});
// Icon
const IconOffice = React.createClass({
getDefaultProps() {
return {
width: '100',
height: '200'
};
},
render() {
return (
<svg className="office" width={this.props.width} height={this.props.height} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 188.5 188.5" aria-labelledby="title">
<title id="title">Office Icon</title>
...
</svg>
)
}
});
ReactDOM.render(<App />, document.querySelector("#main"));
See the Pen SVG Icon in React with default props by Sarah Drasner (@sdras) on CodePen.
Let’s take it a step further, and change out some of the appearance based on the instance. We can use props
for this, and declare some default props.
I love SVG because we now have a navigable DOM, so below let’s change the color of multiple shapes on the fly with fill
. Keep in mind that if you’re used to dealing with icon fonts, you’re no longer changing the color with color
, but rather with fill
instead. You can check the second example below to see this in action, the books have changed their color. I also love the ability to animate these pieces on the fly, below we’ve wrapped it in a div to animate it very easily with CSS (you may need to hit rerun to see the animation play):
See the Pen SVG Icon in React with default props and animation by Sarah Drasner (@sdras) on CodePen.
// App
const App = React.createClass({
render() {
return (
<div>
<div className="switcher">
<IconOffice />
</div>
<IconOffice bookfill="orange" bookside="#39B39B" bookfront="#76CEBD" />
<IconOffice width="200" height="200" />
</div>
)
}
});
// Icon
const IconOffice = React.createClass({
getDefaultProps() {
return {
width: '100',
height: '200',
bookfill: '#f77b55',
bookside: '#353f49',
bookfront: '#474f59'
};
},
render() {
return (
<svg className="office" xmlns="http://www.w3.org/2000/svg" width={this.props.width} height={this.props.height} viewBox="0 0 188.5 188.5" aria-labelledby="title">
<title id="title">Office Icon</title>
<g className="cls-2">
<circle id="background" className="cls-3" cx="94.2" cy="94.2" r="94.2"/>
<path className="cls-4" d="M50.3 69.8h10.4v72.51H50.3z"/>
<path fill={this.props.bookside} d="M50.3 77.5h10.4v57.18H50.3z"/>
<path fill={this.props.bookfront} d="M60.7 77.5h38.9v57.19H60.7z"/>
<path className="cls-7" d="M60.7 69.8h38.9v7.66H60.7z"/>
<path className="cls-5" d="M60.7 134.7h38.9v7.66H60.7z"/>
...
</svg>
)
}
});
ReactDOM.render(<App />, document.querySelector("#main"));
.switcher .office {
#bulb { animation: switch 3s 4 ease both; }
#background { animation: fillChange 3s 4 ease both; }
}
@keyframes switch {
50% {
opacity: 1;
}
}
@keyframes fillChange {
50% {
fill: #FFDB79;
}
}
One of my awesome coworkers at Trulia, Mattia Toso, also recommended a really nice, much more clean way of declaring all of these props. We can reduce repetition of the this.props
here by declaring const for all our uses, and then just simply apply the variable instead:
render() {
const { height, width, bookfill, bookside, bookfront } = this.props;
return (
<svg className="office" xmlns="http://www.w3.org/2000/svg" width={width} height={height} viewBox="0 0 188.5 188.5" aria-labelledby="title">
<title id="title">Office Icon</title>
<g className="cls-2">
<circle id="background" className="cls-3" cx="94.2" cy="94.2" r="94.2"/>
<path className="cls-4" d="M50.3 69.8h10.4v72.51H50.3z"/>
<path fill={bookside} d="M50.3 77.5h10.4v57.18H50.3z"/>
<path fill={bookfront} d="M60.7 77.5h38.9v57.19H60.7z"/>
We can also make this even more awesome by declaring propTypes
on the props we are using. PropTypes are super helpful because they are like living docs for the props we are reusing.
propTypes: {
width: string,
height: string,
bookfill: string,
bookside: string,
bookfront: string
},
That way if we use them improperly, like in the example below, we will get a console error that won’t stop our code from running, but alerts other people we might be collaborating with (or ourselves) that we’re using props incorrectly. Here, I’m using a number instead of a string for my props.
<IconOffice bookfill={200} bookside="#39B39B" bookfront="#76CEBD" />
And I get the following error:

See the Pen SVG Icon in React with spread with error by Sarah Drasner (@sdras) on CodePen.
Even more slender with React 0.14+
In newer versions of React, we can reduce some of this cruft and simplify our code even more, but only if it’s a very “dumb” component, e.g. it doesn’t take lifecycle methods. Icons are a pretty good use case for this, since we’re mostly just rendering, so let’s try it out. We can be rid of React.createClass
and write our components as simple functions. This is pretty sweet if you’ve been using JavaScript for a long time but are less familiar with React itself- it reads like the functions we’re all used to. Let’s clean up our props even further and reuse the umbrella icon just as we would on a website.
// App
function App() {
return (
<div>
<Header />
<IconUmbrella />
<IconUmbrella umbrellafill="#333" />
<IconUmbrella umbrellafill="#ccc" />
</div>
)
}
// Header
function Header() {
return (
<h3>Hello, world!</h3>
)
}
// Icon
function IconUmbrella(props) {
const umbrellafill = props.umbrellafill || 'orangered'
return (
<svg className="umbrella" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" aria-labelledby="title">
<title id="title">Umbrella</title>
<path fill={umbrellafill} d="M27 14h5c0-1.105-1.119-2-2.5-2s-2.5 0.895-2.5 2v0zM27 14c0-1.105-1.119-2-2.5-2s-2.5 0.895-2.5 2c0-1.105-1.119-2-2.5-2s-2.5 0.895-2.5 2v0 14c0 1.112-0.895 2-2 2-1.112 0-2-0.896-2-2.001v-1.494c0-0.291 0.224-0.505 0.5-0.505 0.268 0 0.5 0.226 0.5 0.505v1.505c0 0.547 0.444 0.991 1 0.991 0.552 0 1-0.451 1-0.991v-14.009c0-1.105-1.119-2-2.5-2s-2.5 0.895-2.5 2c0-1.105-1.119-2-2.5-2s-2.5 0.895-2.5 2c0-1.105-1.119-2-2.5-2s-2.5 0.895-2.5 2c0-5.415 6.671-9.825 15-9.995v-1.506c0-0.283 0.224-0.499 0.5-0.499 0.268 0 0.5 0.224 0.5 0.499v1.506c8.329 0.17 15 4.58 15 9.995h-5z"/>
</svg>
)
}
ReactDOM.render(<App />, document.querySelector("#main"));
See the Pen SVG Icon in React by Sarah Drasner (@sdras) on CodePen.
SVG icon systems are beautifully simple and easily extendable in React, have less HTTP requests, and are easy to maintain in the future, due to the fact that we can completely update the output in the future without any repetitive markup changes. We can increase performance by opting into exactly what we need. We can change them on the fly with props for color and even add CSS animation. All of this, and we can also make them accessible for screen readers, which makes React and SVG icon systems a really nice way to add icons to views on the web.
Uh, so you’re recommending using JavaScript to insert an entire SVG literally every single time that an icon is used? So, if you have a table with 100 records, and each record has a pencil icon, a trash icon, and a status icon, you’re going to have JavaScript literally insert 300 SVGs, each complete with all of the path information for that icon?
And how, exactly, is this better than
<use>
(let the browser do the duplicating internally instead of emulating it with JS) or even an old-school background-image SVG spritesheet (forget about duplicating anything, just render an image that’s already in memory, simply at a different offset)?I mean, I get it, it makes sprites easy to customize, but I simply don’t see this ever scaling to any kind of real use. I hope I never have to maintain a codebase that decided to wrap all of its individual sprites in JavaScript components.
No, this is not what I’m recommending. I’m showing you how to use SVG Icon Systems if you’re using React already. Not to convert a view that’s not in React to React in order to use SVG Icons.
I also explain why we’re not using use in this instance in the article. And also why, if you’re using React the way that I’m describing, you don’t have to load every instance for every SVG Icon into the page.
After re-reading the criticism brought up by Agop about 15 times now, I’m not sure Sarah actually addressed it.
As I am reading, Agop seems to be asking how valid this sort of mechanism would be in a real-world website. The example provided, which I’m sure we’ve all designed before, is a tabular set of records with icons for indicating actions (edit, delete, etc.
This is a usage example where sprites or even font icons shine. How well would the this article’s solution function here? It seems like this method would require the SVG to be rendered uniquely for every iteration… so it seems like there would be a scaling issue if your website / app / whatever was heavily reliant on SVG-generted icons as a UI/UX element.
Or is the argument that one would simply not use React for a situation such as the one presented by Agop?
Just as food-for-thought here, GitHub recently switched to SVG icons:
And as an aside, I’m a little concerned about the tone here. There are ways to bring up concerns without a rude tone. I have some friends who have said great things about using a sunlight desk lamp, in how it can help their mood thoughout the day.
This is really about:
<svg><use>
vs<svg><path>
It would be interesting to create some TEST PAGES! 300 icons sounds like a reasonable number to test. Maybe 10 unique icons, used 30 times each.
Page 1: SVG sprite with
<symbol>
s for each unique icon, and<use>
used all 300 times to draw themPage 2: All 300 icons individually drawn with
<svg><path> ...
But then what are the questions? Maybe…
Uh, why would anyone recommend doing that?
Ok, let’s say you have different icons for edit, post, delete. If you’re using use in SVG icon, you’re still rendering the path data in the shadow DOM, even with use. If you’re using an icon font, you’re rendering all of the glyphs for the whole set onto that view whether you need them or not. This is typically many many more icons than you typically need to render that table.
Each technique comes with its correct uses and overhead and it is the developer’s job to pick the tool accordingly. It depends on how many icons your site has and how big that icon font would be. It also depends on how many times you’re using the icons on a given view. I would say the table is actually a smaller use-case across the web than using icons in a menu or a view, and each of these would warrant different techniques. In the case of the table, it may be that an SVG sprite is better, if you don’t need to change the color. If you need to change the color or animate it, that wouldn’t be a great use case anymore. We, as web developers, should be using the right tool for the job, but we should understand the parameters around each before deciding.
Agop mentions: “And how, exactly, is this better than use (let the browser do the duplicating internally instead of emulating it with JS)” – which is addressed first and foremost in the article, which is why I didn’t go into more detail here- it’s already covered. I also explain that with something like webpack you wouldn’t need to load all of the svg and js data into every view in the article.
One thing to understand here is that the article is not prescribing that we throw out icon fonts or SVG use in order to create new SVGs in React- it’s proposing a solution for the lack of support of use in React and how to overcome that in order to create an icon system.
This isn’t valid anymore. I think as of 0.14 or something like that. See
<use>
usage within React in my jsperf tests below.Ago, I understand your concerns about using JavaScript to create views that could and should have already been created in HTML. One thing that is great about JS (and react) is that all this can be rendered with JS on the server. This is a win for reusable code and fast server rendering. Using the technique explain in this article doesn’t prevent you from server rendering.
I have recently done something very similar. But not wrapped the SVG in JS but got react to import the SVG file. This requires more ‘webpack’ setup (SVG-inline-loader) but means the SVG stays within an .svg file.
This combination means that we get the benefits of inline SVG (Style and transition changes), server-side rendering and with client-side caching, which I don’t think has been so eloquently possible before.
If you are using
<svg><use>
with svg4everybody to ajax your svg sprites from a CDN and get around CORS issues (as recommended here https://github.com/jonathantneal/svg4everybody/issues/21), you end up writing a bunch of SVGs to the DOM anyway. I like the suggestion of building it into the JS payload and removing svg4everybody from the equation.If you happen to be using browserify… https://github.com/coma/svg-reactify
I somewhat agree with Agop that this seems like a better solution for more complex, individual illustrations rather than icons used all over a page.
In a React project I’m working on, we’re using a more generic icon component that takes an icon name and size as props (like so:
<Icon name="umbrella" size="big" />
), and internally uses theuse
tag, which is no problem in React 0.14 (and before that withdangerouslySetInnerHTML
, ahem). For cases that require more control than that, your solution looks like a great approach.Also, a little heads-up: The default values for width & height in your
getDefaultProps()
function include the “px” CSS unit, which isn’t valid if you’re going to use it in an HTML attribute.Hi diondiondion,
Great point about the px! Thanks, updated.
So, aside from any use issues, the point of interest here is in use vs path is whether use is really necessary. It seems to me that if you’re still rendering the shadow DOM with use, to discern whether or not it’s not more work for the same thing. Consider this: if you load an SVG spritesheet and then also describe the svg icon, you could be loading more than what is necessary. If you’re loading just the svg icon component, you can theoretically opt-in to the icons you need for a given view.
In case you’re curious if other people are using SVGs inline in this manner, GitHub came out with an article recently detailing how they did just that: https://github.com/blog/2112-delivering-octicons-with-svg.
I certainly think there are instances where this approach isn’t ideal, but I’m not sure that the reasons stated here are taking into considerations the common advantages and disadvantages, and I do still think this could offer a performance and workflow boon, in the case of React and your typical website. But, above all test your use case! Test all the things!
Hi Sarah,
it’s true that in some cases you might end up loading resources more efficiently by including the path directly. In practice I doubt that either of the two approaches is much better or worse in terms of performance than the other. A potentially big advantage of
use
is that modern browsers can cache external svg sprites for future page loads. In browsers that don’t support that (most notably IE & early Edge), they can be inserted into the page using a polyfill like svgxuse.But that actually reminds me of what might just be the biggest advantage of using
path
: its simplicity. It’ll “just work” in all reasonably capable browsers, with no need for polyfills or other workarounds, which is pretty sweet.There’s another advantage of using
use
with external sprites which is quite specific to the product I’m working on, but I’ll mention it anyway: Being able to simply point to a different svg file to change all icons without having to recompile the app. Our app has a different theme, logo & sometimes icon set for each customer, so keeping these things separate & “abstracted away” makes sense for us, but obviously it’s not a requirement shared by many products.It really seems to be mostly a matter of personal preference about workflow and how tightly you want to couple your icons & react components.
Hey diondiondion,
For sure, caching is probably the strongest argument for use here. That’s definitely a good thing to bring up. Is it better than only having to load a couple of icons inline per view at typically >1kb apiece? I’m not sure. That, it seems, would totally depend on the system at hand.
Your use case sounds pretty interesting! With all things being equal, a workflow and abstraction boon while loading a whole different set per customer seems totally appropriate. I can imagine a scenario where you’d be able to bundle components for each customer as well, but that might take more configuration than would make sense. In your case, the sprite sheet might be less maintenance overall, so agreed, it would be a better choice.
I adore this post, Sarah!
One of the reasons I’ve been so bullish about SVG icons is their versatility. I think it’s awesome (and super healthy) for developers to have a wealth of options… client-side via
use
, server-side (a la GitHub’s latest iteration of octicons), or integrated into a project’s JavaScript framework (as you’ve detailed so helpfully). Heck, projects with modest icon needs might be just fine usingimg
!It’s easy to poke holes in any of these solutions for one reason or another. Posts like this one help teams like mine determine what’s best for any given project. SVG is just too complex, powerful and fun to settle for any “one size fits all” solution!
Thanks Tyler! I couldn’t agree more.
I think this is what you were going for. The
id
attribute was missing; pretty sure it’s required (https://www.paciellogroup.com/blog/2013/12/using-aria-enhance-svg-accessibility/)Oh, interesting. Thank you, I didn’t realize that. Updated.
Great article, but the accessibility part is a bit fucked up right now.
You’re using
aria-labelledby
but it’s not necessary and you’re using it in a broken way: you’re using oneid
value (“title”) and React will not change it for each use of the component, so you end up with duplicateid
s all over your page (if you use that icon component more than once, and/or if you use the same id in other icon components).My advice: remove the
id
attribute and thearia-labelledby
attribute altogether, because current screen readers do not need it to read the<title>
element for inline SVG.Also, adding a
<title>
element to your SVG icon does not cater for accessibility. Your icon component should be able to do two things:Hide the icon completely, because the meaning is already spelled out in the neighboring text. That would be:
<svg aria-hidden="true"><path /></svg>
Provide accessible text for the icon, with the ability to change that text for each use, and allowing for internationalization:
<svg><title>[This text must not be always the same]</title></svg>
.This can be done by outputting the
aria-hidden
and the<title>
elements conditionally, depending on whether the code using the component provides accessible text or not (no accessible text provided:aria-hidden="true"
).As a general rule, accessible text is not a description of the graphics, so “Umbrella icon” does not work well as relevant accessible text. It should instead sum up the meaning you’re trying to get across. For instance, if you have
<button><IconUmbrella/></button>
, what is the role of that icon-only button? Does it show a weather forecast? Then you should have:<button aria-label="Show weather forecast"><IconUmbrella/></button>
or<button><IconUmbrella customthingforalttext="Show weather forecast" /></button>
. You want a screen reader saying “Show weather forecast, button”, not “Umbrella icon, button”.Also since you removed the alt text from the graphical element, you can translate it if needed, or change it if you’re using the same graphical element in different places with different meanings. You don’t want a screen reader to read “Umbrella Icon” when you actually need to convey “Afficher les prévisions météo”.
Most of what @fvsch says is true. Unfortunately, this is not (yet):
Which is why most accessibility-oriented SVG examples use a redundant
aria-labelledby
attribute. The value of this attribute must be one or more valid ID references to other elements, and of course each element’s ID must be unique to the page. For an icon with no meaningful child content, you usually also want to give it an explicitrole="img"
so that the browser treats it as a single block, hiding the child markup.Further information on the other points:
SVG 2 allows you to incorporate internationalization in your SVG code with multiple
<title>
elements distinguished by theirlang
attribute. However, as far as I know that isn’t implemented yet in any browsers, so authors still need to select the correct language variant themselves.Even if multi-lingual SVG alternative text was supported, it wouldn’t address the other issue fvsch brings up: the correct accessible text for an icon depends on how that icon is being used in the page. If the icon is inside a button/link, it is more important to describe that element’s function than the icon itself.
Thank you both! I’m learning a ton here. Ok, so here’s what I’m going to do- I’ve updated the codepen with unique props for the title and aria-labelledby and will update the article as well. I’ll probably close the comment thread to this post soon and then write a new article pointing back to this one with everything I’ve learned here through the comments in this post.
I appreciate the feedback very much.
Amelia, do you know which screen readers need the
aria-labelledby
attribute to read the<title>
element? In my tests with fall 2015 JAWS, NVDA and VoiceOver, it wasn’t needed? Windows-Eyes or ZoomText maybe, or older versions of JAWS, NVDA or VoiceOver?Since
<title>
support was good in my tests for in-page SVG elements (separated SVG documents is a different story), I tend to avoid adding extraaria-labelledby
, which get wrong or outdated values really quickly in practice.If one wants to add the extra
aria-labelledby
andid
, it’s probably better to generate a random UUID incomponentWillMount
and use that, so each instance has a workingaria-labelledby
.Keep the React posts coming Sarah! Your article ‘ I Learned How to be Productive in React in a Week and You Can, Too‘ jumped started me into learning the library.
Thanks Marc! Happy that it’s been useful :)
Guys, let’s take a step back here and really look at what we’re doing.
If you render the SVGs directly on the page, say, server-side (like GitHub, yay!), then yes – there might not be much of a difference. The browser might be more efficient doing its own thing with the shadow DOM and
<use>
, or it might be quicker with the full SVGs already in place. I’m placing my bets on it being more efficient with<use>
because it would know that the structure of the SVG is the same each time, allowing for reuse of existing resources related to parsing and rendering. But let’s not diverge…This article is about using SVGs with React, which is why my other comment opened like this:
Using JavaScript is the problem here. React just happens to be one way of using JavaScript to do this. And, in fact, it is a particularly terrible way. Why is it a terrible way?
It’s a terrible way because using React like some kind of SVG template system is awfully slow, especially for complex SVGs! Here, I cobbled together a quick little test on jsperf: http://jsperf.com/react-icons-vs-use/2
Notice how the
<use>
method is way, way faster (like, 500% faster). This should not be any sort of surprise. The<use>
method creates one element. The full SVG method creates, well, a lot.Again, this isn’t necessarily about using a full SVG vs. using
<use>
in regards to writing HTML – it’s simply about using React to construct an SVG. It makes sense to do that when you need a heavily customized SVG here and there (just treat it like a component, duh!), but not when it comes to using SVG icons in general, regardless of whether or not you also happen to be using React.This is primarily a response to this part of the article, which seems to imply “hey, if you’re using React, no need for
<use>
, just use React components!”:Apologies if the tone of my comments comes off as rude. It’s not written to come off as rude – simply as strong reminder that:
Just because you can, doesn’t mean you should.
:)
Great tests, thanks for your hard work there, those are interesting indeed!
Ok, so that makes a lot more sense than what I thought you were saying previously. And it brings up a decent and thoughtful point.
That is certainly a lot faster. But here’s where I have a concern, and I’ll just talk about the use case I have so you can see where I’m coming from. Let’s say you manage a huge site with 50 or so icons. Let’s say you have a view that only needs 3 of said icons. I can see an instance here where not loading all of the 50 either in an SVG spritesheet or in an icon font would come in handy, and the savings there might make it worthwhile.
I can also see an instance, where, like above, I have a more complex SVG that I want to change, either with animation or just to update pieces of it. Let’s say all of my views are in React. I think, in that instance, this approach might still work nicely.
Your tests do provide a really great demonstration of why we shouldn’t do it this way, though. I think you make a great and solid point here.
I can see it both ways, and would still say, choose the right tool for the job.
Sarah, I think we’re pretty much on the same page :)
Like you said, if you have a special SVG – not a fire-and-forget icon, which could be on the page hundreds of times – by all means, make it a component. I’m all for that.
As for the other use case:
Yes, this could certainly make sense. Just keep in mind that an SVG spritesheet with 50 or so icons, after gzip compression, could be mere kilobytes. And, with fingerprinting, you could serve this spritesheet just once with a far-future expiration header. The browser would cache it basically forever, or until you change it (and thus, the filename changes). Then the question becomes: what’s faster, server rendering full inline SVGs and browser parsing each and every one, or using a single SVG which the browser already has in its cache?
Again, like you said: use the right tool for the job :)
I think you make solid points about the caching, and we could probably go back and forth on this forever, but, if you’re properly caching and gzipping your pages and views as well, I’m still not sold. It’s hard to portray a 50 SVG spritesheet as mere kilobytes without also giving a nod to the fact that a typical inline SVG icon, even rendered with React, is likely less than 1kb. If you have a site that goes between a search view and details view without typically needing all of the other icons, I’m still not sure :) At this point we might also be debating a very small difference in task on performance.
@Sarah
Here’s my lazy web question. Are there any tools you can recommend for automatically generating the individual icon components ( preferably with webpack )?
I have been avoiding trying to figure out in React for a while so this post is perfect! Thanks
https://github.com/morlay/svg-simplify
https://github.com/morlay/svg2jsx
Recently, just find a way to do this.
Demo: https://github.com/morlay/svg2jsx/blob/master/examples/entry.js
svgo-loader
orsvg-simplify
could cleanup the svg before using the transformer.For single color icon it will be easy to use.
but for colorful icon like this post shown, may have to compose shapes by yourself.
THIS IS GREAT…. :)
Great info!
This is the best React-centered article I’ve read yet!
I say this because I’m a Frontender who is interested in React but not yet tried messing around with it much. While I may not setup my icon system like this I feel this blog post ranks very high in terms of communicating some basic React concepts that actually gets me excited about React. The writing has opened my mind up to the library… Thanks Sarah ;)
Hey, please check our React SVG icon live generator https://react-svg-icon-live-generator.herokuapp.com/ that created colleague of mine. Any feedback welcome
The aforementioned Github article sparked a discussion for our team to look into using SVG as well.
From my initial tests direct SVG rendering is so much better than icon fonts. What I am doing in our case is just including the full SVG on the page without any Javascript. We use an “include” in the templating language we use (Jade), so syntactically we don’t see the full SVG code on the page. It looks a bit like this:
I am happy with this because if you export a 16×16 SVG from Sketch and you make sure it renders as a 16×16 block with CSS you are 100% sure that it is sharp. Whereas if you export a 16×16 SVG and then mangle it through an icon font generator you are never really sure that it’s sharp.
I think me and my team have spent hours wrestling with icon fonts. This ranges from tasks like debugging icon font output, to telling people how to set up their system to include fontforge/fontcustom to tweaking SVG code to render well as an icon font . I hope these woes will be over now with this new solution.
Some tips: it’s best that you export your SVG icons at a certain size and define it explicitly:
You can then “color” SVGs with CSS as follows:
Great write up Sarah! I’m using a similar approach in some Rails projects with https://github.com/jamesmartin/inline_svg
I did an SVG icon system recently, that had to be compatible with React and plain JavaScript (React is used for multiple SPAs embedded in pages, there’s also legacy JS code). I ended up using webpack-svgstore-plugin for generating the SVG bundle and then consuming it from React like:
<SvgSymbol name={‘icon-name-is-the-src-file-name’} />
or from server side templates (Jinja2 based) like:
{% svg_symbol(‘icon-name’) %}
Helper tags simply emit <svg><use…> stuff and reference icon like “/path/to/bundle.svg#icon-name”.
This can be easily extended to support multiple bundles via optional param bundle={‘bundle-name’}
A problem with directly importing SVGs in React and relying on Webpack or something to bundle them into JS is the resulting icon system is usable only from React land. I see vendor lock-in for such trivial thing as a bigger problem than the performance issues mentioned above.
I think all my Pure components had they’re feelings hurt when you called then “dumb” ;(
Hey Sarah,
thanks for the writeup and the discussions with Agop.
One quick point about #a11y here. In the described scenario you’re bringing in several SVG elements which all include a title element with the id “title”.
I just tried it with VoiceOver. Assuming you’ve got several different icons included like this in a page and all of them include
VoiceOver will read the title of the first appearing icon for all of them, as ID’s should be included only once inside of a document. Setting unique ID’s is needed for this in combination with
aria-labelledby
to work properly.Thanks. :)
Hi Stephan,
Yeah, if you look up in the comments and the post, this was all addressed and the post was updated. I can update all of the other codepens, though, to avoid future confusion. I only updated the one that was the reference for the accessibility.
Thanks for looking out!
Hi Sarah Thanks for the share