I’ve long advocated SVG icon systems. Still do. To name a few benefits: vector-based icons look great in a high pixel density world, SVG offers lots of design control, and they are predictable and performant.
I’ve also often advocated for a SVG icon system that is based on <symbol>
s (an “SVG sprite”) and the <use>
element for placing them. I’ve changed my mind a little. I don’t think that is a bad way to go, really, but there is certainly a simpler (and perhaps a little better) way to go.
Just include the icons inline.
That’s it. Sorry if you were hoping for something fancier.
Like this:
<button>
<svg class="icon icon-cart" viewBox="0 0 100 100" aria-hidden="true">
<!-- all your hot svg action, like: -->
<path d=" ... " />
</svg>
Add to Cart
</button>
Or perhaps more practically, with your server-side include of choice:
<button>
<?php include("/icons/icon-cart.svg"); ?>
Add to Cart
</button>
Like I said:
<?php include "icon.svg
<% render "icon.svg"
<Icon icon="icon"
{% include "icon.svg"Putting right into markup is a pretty 👍 icon system.
— Chris Coyier (@chriscoyier) May 31, 2017
Advantage #1: No Build Process
You need no fancy tooling to make this work. Your folder full of SVG icons remain a folder full of SVG icons. You’ll probably want to optimize them, but that’s about it.
Advantage #2: No Shadow DOM Weirdness
SVG icons included as a <use>
reference have a shadow DOM boundary.
This can easily cause confusion. For example:
var playButton = document.querySelector("#play-button-shape");
playButton.addEventListener("click", function() {
alert("test");
});
That’s not going to work. You’d be targeting the path in the <symbol>
, which doesn’t really do anything, and the click handler is kinda lost in the cloning. You’d have to attach a handler like that to the parent <svg>
, like #play-button
.
Likewise, a CSS selector like:
.button #play-button-shape {
}
Will not select anything, as there is a Shadow DOM boundary between those two things.
When you just drop inline SVG right into place, there is no Shadow DOM boundary.
Advantage #3: Only the Icons You Need
With a <use>
/<symbol>
system, you have this SVG sprite that is likely included on every page, whether or not they are all used on any given page or not. When you just include inline SVG, the only icons on the page are the ones you are actually using.
I listed that as an advantage, but it sorta could go either way. To be fair, it’s possible to cache an SVG sprite (e.g. Ajax for it and inject onto page), which could be pretty efficient.
@Real_CSS_Tricks how cache-friendly is SVG <use>? #SVG #CSS
— Samia Ruponti (@Snowbell1992) June 7, 2017
That’s a bit of a trick question. <use>
itself doesn’t have anything to do with caching, it’s about where the SVG is that the <use>
is referencing. If the sprite is Ajax’d for, it could be cached. If the sprite is just part of the HTML already, that HTML can be cached. Or the <use>
can point to an external file, and that can be cached. That’s pretty tempting, but…
Advantage #4: No cross-browser support concerns
No IE or Edge browser can do this:
<use xlink:href="/icons/sprite.svg#icon-cart" />
That is, link to the icon via a relative file path. The only way it works in Microsoft land is to reference an ID to SVG on the same page. There are work around’s for this, such as Ajaxing for the sprite and dumping it onto the page, or libraries like SVG for Everybody that detects browser support and Ajaxs for the bit of SVG it needs and injects it if necessary.
Minor Potential Downside: Bloat of HTML Cache
If you end up going the sprite route, as I said, it’s tempting to want to link to the sprite with a relative path to take advantage of caching. But Microsoft browsers kill that, so you have the choice between:
- A JavaScript solution, like Ajaxing for the whole sprite and injecting it, or a polyfill.
- Dumping the sprite into the HTML server-side.
I find myself doing #2 more often, because #1 ends up with async loading icons and that feels janky. But going with #2 means “bloated” HTML cache, meaning that you have this sprite being cached over and over and over on each unique HTML page, which isn’t very efficient.
The same can be said for directly inlining SVG.
Conclusion and TLDR: Because of the simplicity, advantages, and only minor downsides, I suspect directly inlining SVG icons will become the most popular way of handling an SVG icon system.
One (somewhat surprising to me) downside of this approach that we found if you’re rendering your page with React: if you have a lot of icons on your page, the extra work it takes for React to render out all of the individual elements inside an icon can add some noticeable load time over having a
<use>
tag.If you’re using React (and/or friends) does directly inlining basically become the same as dropping a big ol’
<symbol>
chunk into your html? You kinda just end up with a sprite that lives in a js bundle instead.Am I supposed to be splitting icons into separate bundles? Has this problem been around long enough that there’s a RightWay™ to handle it? Is it not enough overhead that it’s an issue?
I’m not sure what React / JS framework has to do with it, but basically the differences outlined in the article are relevant regardless of the framework.
The best way to approach SVG’s has always centered around your use case. There’s no “right way”, because various approaches have their pros and cons. For instance, on the site I’m working on now, I need to be able to animate the paths of my SVG. If I was using an externally referenced ‘ myfile.svg#foo’ approach, I could take advantage of caching across pages but would limit my ability to animate the paths within that (Shadow DOM issues).
To properly answer your question, you’d need to evaluate all the different SVG approaches, and what your needs are.
I inline a sprite on first load, but also park it in localStorage and inline it from there for subsequent pages using inline script. No noticeable jank.
Can you expand on this technique?
+1 Russell Bishop’s question.
I’ve been using since a while, a pug/jade mixin to include svg on my HTML file on compile time, I have to say that is not as mantainable as I would like to be, but is powerfull and flexible
You need to mantain a “case” statement on the mixin with a line for each icon and convert you SVG to pug files
Here is a small example:
https://codepen.io/albpara/project/editor/DBaYKn/#
cool cool cool
If you’re making a button with an SVG icon, the image should be inside a
<button>
anyway, regardless of how you’re displaying it.Can you not make an ‘:after’ in the button as the icon with the svg as the background? No functionality on the svg though :(
If you didnt want any svg functionality you could do a classic and make an :after or :before on the button and make the svg the background of that. Would be nice if we could still style and edit svgs this way, but yeah… :(
You can, but I would argue that the SVG — whether you’re displaying it by inline or background image — should be its own element, inside the button element.
Uhm why?
I view it as separation of style and functionality.
Any good solutions for angular2 basehref issue with svg use # href ?
One example of a current standard is a short html snippet (like
<i>
) with a class name (like<i></i>
).If it’s just browser support, I can wait 6 months or a year, and it’s no longer an issue for any particular system? (I have waited out many, many hacks and browser bugs this way, works like a charm)
How would your code base be to work with in the IDE (not to mention file size issues) with literally 10’s of thousands of lines of inline SVG code all through your templates, JS files, etc…? (perhaps you mean for just simple websites?)
How is easy is it to maintain changes in an icon that is inlined compared to other methods? (eg,
<i></i>
)How is inlining better than just using ?
You seem to have specific goals to achieve with this method without stating them. Do you intend to style specific elements of the icon with CSS that only inlining allows for? Perhaps your goal set covers some other specific needs that regular images or background images don’t work well with? (browser support issues aside)
I keep all icons in one XML-file which become php-variables on every page by simpleXML where I echo the variables them inline. This way an icon can be replaced by text, a comment or emptiness. It’s like in the Sovietunion, only with icons instead of humans—and with XML!
I’m not ready to answer to that yet, but it sounds ok!
Totally agree Chris. I was using symbol / use icon system for years now and very recently just realised that the whole gulp task, icons separation, javascript dependency for IE and compilation just for the sake of 4 social icons on the page is a massive overkill. Switched exactly to your new approach — inline everything. Thanks for write up.
This is a non – issue. I have been using SVG icons extensively for years.
Just use
<img>
tags, specify viewBox attribute in SVG file /tag to mitigate browser compatibility issues, and make sure the correct mediaType is being served in the HTTP response headers!The two downsides there being…
But if those things aren’t of concern, yep, that’s even easier.
I’ve tried a few different methods regarding SVG Icons, and I always seem to come back to using the
<img>
tag as well when I know that there won’t be any scripting needed.Each icon may initially require a separate HTTP Request, but that can be a positive if the icon is used globally and the browser caches the SVG image file.
Wouldn’t multiple requests be a non issue if your server is configured with http2 ?
You can not change the appearance of an svg as an tag.
Using external recource with SVG is supported by Edge 13 and newer (https://developer.microsoft.com/en-us/microsoft-edge/platform/changelog/desktop/10547/).
There’s another polyfill which doesn’t use browser sniffing: https://github.com/Keyamoon/svgxuse.
This is what I have been using recently, and it works pretty well IE. There is some delay until the icons come into place, but I think that’s acceptable.
I too love svgxuse.
I’ve been using this approach in my current project.
Maintainability is a non-issue, presuming you are using some sort of templates – preferably server-side. (I pity everybody struggling with the poor performance of client-side templates. While I grok the advantages of data from some API made into markup in the browser… for probably 90% of projects it is a performance-sucking mistake.)
So I do this in a hybrid-mobile app using the Rhodes framework. There is a Rails(-like) server in the app and ERB (I know… I find ERB painful too, but it’s what is available) templates. The icons are saved from Illustrator with entity styles, so you can easily override with CSS. Layers are named semantically, so you get semantic IDs. A simple pre-build workflow changes
id=
toclass=
in the SVGs. So, now, I can change colors, animate, whatever using semantic CSS class names.The
.svg
files get renamed to_whatever.svg.erb
and placed in a directory accessible to the template engine. So, now each icon is an ERB partial.Yes, with hand-editing (or a workflow) I can include partials or other ERB markup inside the icons. I actually do this currently on one icon, which is a static circular progress bar. (Static because progress would never change while viewing the page with the icon.)
progress_percent
is passed-in to the partial as a local.I have plans to do i18n of text inside the SVGs. Each phrase is placed on a layer, and the layer given a meaningful name that can be used to look-up the translation from a table. The same pre-build workflow can be used to replace the content of specially-marked
<text>
elements with e.g.<%= t :t_submit_btn_lbl %>
(wheret
is my translation helper).Template render time is a non-issue in this particular environment, as the templates are compiled to Ruby and then the Ruby compiled to Ruby bytecode at build time. At run time it is just executing Ruby bytecode and once a file is loaded it stays loaded.
One further benefit is that
<use>
requires a little more CPU by the browser than direct shape drawing:Yeah I’ve been doing this for quite some time. Big fan of this solution.
Thanks for the affirmation!
I still prefer the SVG sprite inlined inside the CSS to be used either explicitly with
<i class="[size] [color] [type] icon">[Fallback]</i>
. The only disadvantage compared to putting the SVG into the HTML is that you need an extra statement per color. Being able to reuse icons without adding more size to your page is definitely an advantage.The thing that holds us back from using inline icons is that the CMS we use doesn’t happily support them. We would also need our users/clients to be inputting icons when adding CTA boxes etc. We use an SVG sprite and a simple class name to add icons where desired with
::before
and::after
elements – built with the technique https://www.liquidlight.co.uk/blog/article/creating-svg-sprites-using-gulp-and-sass/Not sure if it could be considered the best method in terms of performance, but after many different approaches I have landed on what I think is my preferred implementation when using React: each icon is simply defined as an inline within its own component. It makes using and updating icons really simple and doesn’t sacrifice any SVG functionality. Plus it makes extending the functionality with react really simple (for instance, I have a file placeholder icon with a text element that changes based on the filetype, which I simply pass in via a prop).
I am using all my svg’s as inline where i need them from an php function call.
But what about clipping? You need IDs for the clipPath. And if you place an svg icon multiple times, you have to change all clipping IDs. Do you know a better way?
That’s basically how I implement vue-awesome :D
Can’ t it be a problem when you have a somewhat complex svg symbol that is used a lot on a page? For instance, I have a list of articles, all displayed with a little icon before the title to “categorize” the article. If I inline the svg images for each article, my html file will be huge. Is it a valid issue to stay on the USE system ?
This is my main question as well. In my situation, my file size would explode exponentially by inlining all the icons, it’s simply not a one-size-fits all approach.
The solution that inlining seems to solve is to allow control over the SVGs. But if you don’t care to do that, then SVGs as backgrounds, or as ::after/::before inserts, etc… (every other system) work just fine, or even better than inline SVG.
To state emphatically inline SVG is the best way is to ignore many use cases where it’s literally the worst way.
Exactly during the last month, I started to acquire a better understanding of the best way to manage icons and images for my personal blog. In particular I learnt that, with HTTP2, things like image sprits will no longer be necessary.
I’m not sure that will shake out to be the case or not. We need more data and more test cases, I think. I’ve definitely heard conflicting talk. I don’t think it’s as easy as “if you’re using HTTP/2, never concatinate anything.”
We mostly use a two tier approach.
Common Icon/Images SVG
For images that are used on most pages, menus,logos etc we tend to use an inline sprite file. Yes, it has a small build process but has some advantages for us: We can use it to add language specific accessible text from our CMS which seems less easy when using individual SVG file?s
To achieve this multilingual/accessible text another way might be to use multiple svg files e.g.
svg-en.svg
,svg-fr.svg
for multiple languages?Or perhaps use a
<switch>
in the svg file e.gBut using
<switch>
seems less easy to maintain than keeping it in the CMS?Individual & Less Used SVG
For individual and or less used single SVG images, maybe illustrations or some such, we may load them via an include and inline them that way. We’d probably use a language switch in this case.
Does anybody have any thoughts or comments on this approach? It also strikes me that it might be possible to include/inline the SVG file at first launch but then store it with a service worker and pull it back from the cache in the future? Not sure if this has any benefits though.
This is the approach we have been using in production. It’s easy to style and animate.
Worth mentioning that if you do use the same icon multiple times on a page Gzip compression will stop you having to worry about file size. Which takes away one of the benefits of
Seems cool :) but I often need svg as a background image, how about those, make a sprite for now?