<use> tags offer a no compromise solution.
Scott Jehl made a
great interactive demo of this.
Thanks to everyone on HN and to Scott for the feedback!
<svg> elements
as well as some feed-reader-unfriendly CSS and JavaScript in this post.
- 2024-07-29: Add a note about pending updates and a link to Scott Jehl's
<use>demo - 2024-07-25: Add list of properties to each subheading, show
<use>tags
With SVGs on the web, you get to choose any two:
-
Stylable:
can be colored with CSS (including foreground, background,
:hoverstates, etc) - Cacheable: load once, use on other pages is free
- Dimensional: has an intrinsic width and height
Referencing SVGs from an <img> element
- stylable
- cacheable
- dimensional
An SVG referenced via an <img> tag works like any other image.
If you load the same image on multiple pages, it will be cached and not re-downloaded.
It can be used without specifying width and height to be displayed at its natural size,
or with only one of the two specified for it to be displayed proportionally,
or can be made to automatically fit its container.
Other image types like PNG or JPEG files only support being referenced this way in the first place.
Displaying an SVG via an <svg> tag
- stylable
- cacheable
- dimensional
An SVG defined via an <svg> can have its objects styled with CSS.
The CSS properties that apply to SVGs are sometimes different than those that apply to HTML elements,
like fill instead of color,
but it's all CSS in the end,
and you can use the same stylesheet to style SVG elements along with the rest of your page.
An SVG defined this way has another advantage:
it can use the currentColor value for its fill and stroke properties,
which means that it will automatically inherit the
text
color
of its parent element.
The shapes support :hover states and other CSS pseudo-classes, too.
In fact, they can have styling dynamically applied via JavaScript just like any other element!
It's implemented in the diagram above,
try hovering over any of the text labels.
Of course, placing the SVG inline to the HTML means it cannot be cached,
any more than a <p> or a <div>
is cached when it's repeated on multiple pages.
This is almost never a problem with text,
which tends to be very small and not be repeated on multiple pages,
but might waste bandwidth if used for SVGs used often, like a logo or icon.
Displaying an <iframe> with an <svg> inside it
- stylable
- cacheable
- dimensional
It's too bad that an inline <svg> cannot be cached,
because it would be nice to be able to style logos and icons with CSS,
and those are the things that are most likely to be repeated on multiple pages.
To solve this, we could create an HTML file that contains only the <svg> tag,
and reference it on the site via an <iframe>.
This solves the cachability problem, but introduces a new one:
an <iframe> does not have an intrinsic width or height,
at least as far as the browser can tell before rendering it.
You can hack around this dimensionality problem by
measuring the <iframe> in JavaScript after it has loaded,
although this requires JavaScript and causes a
flash of unstyled content
and an extra reflow.
You could even measure it in advance and store its dimensions somehow,
so that your site can set width and height on the <iframe> tag,
depending on your requirements.
Apparently I think this is pretty neat, though, because I have referenced iframes a few times before, and I've run into the dimensionality problem with iframes in the past.
Other options
It's also worth mentioning a few other ways to reference SVGs:
-
You could create two SVG files, one for the normal state and one for the hover state,
reference them via
<img>tags, and swap them via CSS when the user hovers over the element. At least as of the time of this writing, this is what I do for my site logo in the header. This works well if all you need are the two states of hover and normal. -
You could use
the
<object>tag, which is like an<iframe>but for multimedia objects like SVGs. It has the same dimensionality problem as the<iframe>tag, and is not stylable with CSS from the parent document, but can be manipulated with JavaScript from the parent. -
If you're using an SVG for an icon multiple times on the same page,
you can embed an
<svg>tag with the icon inline in the HTML, and use a<use>tag to reference individual symbols from it. I do this on this page for the check- and x- mark icons and . This mitigates the cachability problem for icons used more than once on the same page, like say a trash can icon on a list of items, but the icons are still not cacheable accross pages. (One way to achieve full cacheability is to fetch the SVG via JavaScript and inject it into the page.)