A Complete Guide to SVG Fallbacks

Avatar of Amelia Bellamy-Royds
Amelia Bellamy-Royds on (Updated on )

If you’re using SVG and are worried about unsupported environments, this is the guide for you. There is no single solution, since how you use SVG dictates the fallback.

UGURUS by DigitalOcean for agency owners

UGURUS offers elite coaching and mentorship for agency owners looking to grow. Start with the free Agency Accelerator today.

Here at CSS-Tricks, there’s a lot of information telling you how wonderful SVG is. And as much as we want to convince you that SVG is for Everybody, SVG isn’t as widely used as we would like. In fact, some people still (literally) don’t get SVG.

Maybe they are stuck on an office computer that hasn’t been updated since the IT guy quit six years ago, maybe they’re using a second-hand refurbished phone they can’t afford to upgrade, or maybe they just don’t understand or don’t care about updating to a new browser. Whatever the reason, approximately 5% of users browsing the web are doing so with web browsers that can’t display SVG — more in the USA (according to caniuse data).

Some tech-oriented web sites (like this one) can afford to say that they only support modern web browsers. But for most sites, you can’t ignore 1 in 20 potential customers. If you want to give the 95% of users on modern browsers all the benefits of SVG, but still provide a functional experience for the others, you need a fallback plan.

The guide is written by Amelia Bellamy-Royds and me, Chris Coyier. Amelia and I presented at the same conference together. We both covered SVG, yet neither of us SVG fallbacks comprehensively. It’s such a huge topic, after all. While I’ve covered SVG fallbacks before, it’s been a few years and we figured we could do that subject better justice now. Here we go!

What KIND of fallback do you need?

Before getting into the technical options for implementing fallbacks, it helps to stop and think of what kind of fallback you need:

  • No fallback. If the SVG is an icon whose meaning is clearly expressed by a text label, you may be able to let that icon disappear without compromising the site’s functionality.
  • Text fallback. If the SVG is an icon whose meaning could be expressed by a text label, maybe all you need is to ensure that alt text shows up in its place.
  • Image fallback. This is what most people think of as an SVG fallback: a PNG or GIF image that represents the same graphic, just with a larger file size and poorer resolution.
  • Interactive fallback. For replacing animated and interactive SVG, a PNG may not do justice. You’ll need a graphics language with an interactive DOM.

On the interactive front, your options are limited. You could convert the SVG to Flash and then rewrite all your interaction code in ActionScript. Or you could use Raphaël to both draw and manipulate the graphic. It provides a single JavaScript interface to both SVG and the VML vector graphics supported in Internet Explorer 6-8. Which means that Raphaël doesn’t help with old mobile browsers, but it does cut down the number of unsupported users significantly.

The rest of this post covers the ways to create text and image fallbacks for your SVG. Your options will depend almost entirely on how you’re including that SVG in the web page in the first place: as an embedded object, as inline SVG code, as an image in the HTML, or as an image in the CSS.


Fallback for SVG as <img>

SVG can be used like this:

<img src="image.svg" alt="">

Here are some options for browsers that don’t support that.

How to Test for Support

Even though this test is supposed to test for the <image> element within SVG, thanks to some testing we’ve proven it works for SVG used as the source for <img> elements as well. It’s this easy:

function svgasimg() {
  return document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image", "1.1");
}
Source Swapping

The test above is what the SVGeezy library uses to do SVG-as-img fallbacks. If the browser fails this test, it will swap out the SVG with a PNG as needed. As in <img src="image.svg"> turns to <img src="image.png">. You create the PNG versions yourself and have them available in the same directory.

  • Advantages: It’s easy. It works. You can even write your own source-swapping JavaScript pretty easily.
  • Disadvantages: It’s likely the non-supporting browsers will download two images, which is a performance penalty. It will download the SVG version (at least enough to know it can’t use it) and then the PNG version.

Here’s a very simple no-dependency example of source swapping:

Swapping in a fallback image, in Internet Explorer 6(!)

If for some reason you couldn’t get this into external JavaScript and had to do it inline…

<img src="image.svg" onerror="this.src='image.png'; this.onerror=null;">

If you were doing feature detects with Modernizr and also utilizing jQuery on a project, it could be as simple as this:

if (!Modernizr.svg) {
  $("img[src$='.svg']")
    .attr("src", fallback);
}

SVGInjector is a JavaScript library that can help with <img> fallbacks in specific scenarios. It’s main purpose is to replace <img> with inline SVG.

<img class="inject-me" src="image-one.svg">
var mySVGsToInject = document.querySelectorAll('img.inject-me');
var injectorOptions = {
  pngFallback: 'assets/png'
};
SVGInjector(mySVGsToInject, injectorOptions);

SVGMagic is another JavaScript library that swaps out sources with PNG versions, including SVG used in <img>, background-image, or even inline. It’s biggest advantage is that it creates the PNG version for you automatically, by request to a third-party server. So just beware the dependencies (jQuery and third-parties) and test for speed and reliability.

The <picture> Element

The <picture> element allows for fallback images when the browser doesn’t support a specified image format.

<picture>
  <source type="image/svg+xml" srcset="image.svg">
  <img src="image.png" alt="">
</picture>

Unfortunately that’s not much use when support for SVG is much better than support for <picture>.

You can polyfill the picture element though, with Picturefill. Sara Soueidan has an article that covers this.

  • Advantages: Not only can you get fallback images, you can specify different fallbacks at different media queries, as part of the picture syntax. Meaning differently sized fallbacks for different screens, art direction, etc.
  • Disadvantages: Polyfill needed. To avoid double-downloads, you need to skip the src on the <img> within <picture>, which is invalid and means you’ll need the polyfill forever.
The <image> Trick

With some inline SVG trickery we can pull this off:

<svg width="96" height="96">
  <image xlink:href="image.svg" src="image.png" width="96" height="96" />
</svg>

Be sure to add sizing attributes to the image so that it fills up the entire SVG, and then size both svg (for modern browsers) and image (for old ones) in your CSS.

Besides excluding a few older browsers that can support SVG images but not inline SVG, the main limitation here is that it will be a lot more work to get the images to display the correct size on a fluid site. You’ll need to use the same tricks used to get an SVG to scale to a consistent aspect ratio, instead of just setting width: 100%; and letting the height adjust automatically.

  • Advantages: Requires no JavaScript or any other dependencies.
  • Disadvantages: Triggers multiple (aborted) requests in IE. Older iOS can display fallback even though SVG is supported. Tests.

Fallback for SVG as <object>

As inline SVG has become better supported, SVG as <object> has fallen out of favor. That’s unfortunate from a progressive enhancement/graceful degradation perspective, because this is the easiest way to provide fallback content.

The child content of the <object> element is displayed if the object itself cannot be. That content can be any html content: images, formatted text, even another object (for example, one containing a Flash version of your animated SVG).

<object type="image/svg+xml" data="svg-ok.svg">
  <img src="svg-no.png" alt="No SVG support">
</object>
<object type="image/svg+xml" data="svg-ok.svg">
  <p class="warning">
    Your browser does not support SVG!
  </p>
</object>

See a simple test of this.

Another progressive-enhancement benefit of <object>: some users on Internet Explorer 8 (and under) will be able to see the SVG! Objects trigger plug-ins, and the no-longer-updated Adobe SVG Viewer plug-in is still available for download for old IE.

Fallback for SVG as CSS background-image

For most CSS properties, you can trust in CSS error handling to get the browser to ignore a new syntax and apply a value declared earlier in the cascade. However, the syntax of a CSS rule using an SVG image file is perfectly correct for the old browsers. So they apply the rule, download the file, but then don’t know what to do with it.

The trick here is to find syntax that is supported by (nearly all) browsers that support SVG, but not by the older browsers. Here’s the magic:

body {
  background: url(fallback.png);
  background: url(background.svg),
    linear-gradient(transparent, transparent);
}

This combines two features that, together, target the perfect combo. If a browser supports both multiple backgrounds and linear gradients, it supports SVG too. So here we declare the SVG with a completely transparent linear-gradient. If that fails in an older browser, the fallback declared above will kick in.

You might add a slight performance hit on modern browsers from having to calculate the transparent gradient, but it’s probably negligible.

Replacement background images, in Internet Explorer 7
Replacement background images, in Android 2.2

Fallback for inline <svg>

Inline SVG is popular for many reasons. All the code to draw what needs to be drawn is either right there in the markup (reducing requests), or referenced from a file that can be cached. You have lots of control over SVG that is inline. It’s in the DOM, so it can be controlled with CSS and JavaScript from the same document.

How to test for support

There are some great tests from Modernizr that we can look at. Your best bet for inline SVG, extracted out into a function, is this:

function supportsSvg() {
  var div = document.createElement('div');
  div.innerHTML = '<svg/>';
  return (div.firstChild && div.firstChild.namespaceURI) == 'http://www.w3.org/2000/svg';
};

This tests if the HTML parser (which is what is used to parse content sent to innerHTML) can correctly generate an SVG element, basically by creating an element, injecting SVG, and testing the namespace.

As with <object>, a browser that doesn’t recognize <svg> will just ignore your SVG markup and treat its contents as HTML. But its not that simple. With <object>, browsers that do support the object will know to ignore the child content. With inline SVG, there’s no such built-in fallback. However, there are a few workarounds you can use.

Plain text fallback inside inline SVG

You can include plain text inside your inline SVG code, and it will be ignored by any browser that supports SVG, because SVG text must be contained in a <text> element. You can even include hyperlinks within that text.

<svg viewBox="-20 -20 40 40">

  <!--Text Fallback-->
  I'm sorry, your browser does not support

  <circle fill="limegreen" r="19" />
  <path stroke="forestgreen" fill="none" stroke-width="6"
        d="M-12,3 L-3,10 11,-12" />
  <text dy="0.35em" text-anchor="middle" font-weight="bold"
        font-size="18px" font-family="sans-serif"
        fill="indigo">SVG</text>

  <!--Fallback with links-->
  Please upgrade to a <a href="http://browsehappy.com/?locale=en">modern browser</a>.

</svg>
Plain text fallback, as it appears in Android 2.3

Things to note:

  • The SVG text (the word “SVG” in this demo) becomes part of the fallback text. We’ll talk about how to avoid this in a moment.
  • The SVG <title> text does not become part of the fallback text. That’s because the no-SVG browsers interpret this as an invalid second HTML title element, and ignore it.

You cannot include any other HTML content within inline SVG: when the HTML parser in a modern browser reaches an HTML tag, it assumes that you forgot to close your <svg> tag, and closes it for you. Which means that (a) your SVG is corrupted and (b) your fallback text is visible in modern browsers. The only reason you can use links (<a> tags) is because they are valid tags in SVG, but don’t draw anything themselves.


Another approach here would be to start with HTML text, and swap it out with inline SVG should you detect with JavaScript it is supported. Imagine a “like” button in HTML:

<button aria-label="Like">
  <span class="inline-svg" data-xlink="#icon-heart">♥</span> Like
</button>

The span with the ♥ is the fallback there. The data attribute is what we’ll use for a fallback, in this case, a <svg>/<use> element.

if (supportsSvg()) { // see test above
  var inlineSvgs = document.querySelectorAll('span.inline-svg');

  for(i = 0; i < inlineSvgs.length; i++) {
    var span = inlineSvgs[i];
    var svgns = "http://www.w3.org/2000/svg";
    var xlinkns = "http://www.w3.org/1999/xlink";
    var svg = document.createElementNS(svgns, "svg");
    var use = document.createElementNS(svgns, "use");

    // Prepare the <use> element
    use.setAttributeNS(xlinkns, 'xlink:href', span.getAttribute('data-xlink') );

    // Append it
    svg.appendChild(use);

    // Prepare the SVG
    svg.setAttribute('class', "inline-svg");

    // Set a title if necessary.
    if (span.getAttribute('title')) {
      svg.setAttribute('title', span.getAttribute('title'));
    }

    // Inject the SVG
    span.parentNode.insertBefore(svg, span);

    // Remove fallback
    span.remove();
  }
}
HTML formatted-text fallback within inline SVG

You can use HTML text-formatting markup without ruining the SVG by including it within SVG <desc> (description) tags, which allow content from other namespaces.

<svg viewBox="-20 -20 40 40">

  <desc>
    <p class="warning">
        Fallback text.
    </p>
  </desc>

  <!-- ... SVG content ... -->

</svg>
Formatted text fallback, as it appears in Internet Explorer 8

Things to note:

  • The main purpose of the <desc> tag is to provide alternative text description. Make sure the content of the tag makes sense to a screen reader.
  • In order for <desc> to be recognized by accessibility technologies on all browsers, it should be at the top of the SVG, right after the <title>.
  • You should also be able to use an SVG <metadata> tag to include all sorts of arbitrary markup without affecting the SVG or its alternative text. However, this doesn’t work in browsers tested (they insert implicit </svg> end tags).
  • The SVG text content still gets included in the fallback text.

You could even include an <img> tag with a fallback image within the <desc> and it would display correctly in the old browsers without breaking your SVG in the new browsers. Don’t do this! Although modern browsers do not display the fallback image, they do download it, and we’re always trying to avoid double-downloads.

background-image fallback for inline SVG

A possibility for an inline <svg> fallback is to set a background-image that is only used if the browser doesn’t support inline <svg>. So, using the test above, you give yourself a class to work with:

if (!supportsSvg()) {
  document.documentElemement.classList.add("no-svg");
  <em>// or even .className += " no-svg"; for deeper support</em>
}

Then say you’re using some inline SVG like this:

<button>
  <svg class="icon icon-key">
    <use xlink:href="#icon-key"></use>
  </svg>
  Sign In
</button>

You could use that new class to apply a background-image if needed:

html.no-svg .icon-key {
  background: url(fallback-key.png) no-repeat;
}

Demo:

You’ll have to create the PNG and size it yourself. There is a walkthrough article here on CSS-Tricks about using Grunticon to automate all this.

For the deepest possible support, you’d wrap the <svg> in a <div> and apply the background to that, so even if the browser totally rejects the SVG element, the fallback will still work on the known <div> element.

<img> fallback within inline SVG

We covered this above in “the <image> trick” section for inline  fallbacks, but since this is really using inline SVG, let’s cover it in more detail here. To get an image fallback in SVG, without extra downloads, you need a way of including an image:

  • That old browsers will recognize as valid HTML
  • That the HTML 5 parser will allow inside <svg>
  • That modern browsers will not interpret as an image to download.

As strange as it may seem, the SVG <image> element serves this purpose. The SVG <image> element is used to embed other image files within SVG. However, within HTML, every browser tested recognizes <image> as a non-standard synonym for <img>. In SVG, you specify the URL of the image file with the xlink:href attribute. In HTML, you specify it with the src attribute.

In most browsers, therefore, it is sufficient to include an <image> tag with a src attribute (pointing to your fallback image) inside your inline SVG: the old browsers will download the fallback, the new browsers won’t. Except for Internet Explorer, which downloads the fallback image even when it doesn’t display it. The solution is to put a null xlink:href attribute on the element. The IE developer tools still show it requesting the fallback, but it aborts almost immediately (<1ms in IE11 or IE10/IE9 emulation mode), before downloading anything. It looks like this:

<svg viewBox="-20 -20 40 40">
  <!-- SVG code snipped -->
  <image src="fallback.png" xlink:href="" />
</svg>
Image fallback, as it appears in Internet Explorer 8

Now to address that one remaining fallback problem: the text from the SVG that gets dumped to the screen. It somewhat ruins our perfect fallback image replacement. We can use CSS to help us fix this, but it’s not straightforward. We can’t use a simple svg text { display: none; } because that would ruin the SVG graphic. You could use the detection JavaScript to set classes that will hide or display the text accordingly. But even then, you’ll be stymied by IE8’s non-standard approach to unrecognized tags: they are always treated as empty void tags with no child content. You’ll need to adapt the HTML5 Shiv code to include all the SVG tags if you want to be able to treat them as stylable containers.

A no-JavaScript option is to wrap all your SVG graphics code (except the fallback <image>) inside an <a> element. Without a hyperlink destination, an <a> element in SVG acts exactly like a <g> grouping element, so there is no harm to your SVG code. And because IE8 recognizes <a> as a valid container element, any styles applied to it will apply to all the text within it.

So the trick is then to apply styles that will hide an HTML link’s contents, but won’t have an effect on an SVG group. Absolute positioning and hidden overflow do the trick:

.hide-on-fallback {
  display: block;
  position: absolute;
  left: -100%;
  height: 0;
  width: 0;
  overflow: hidden;
}
Image fallback, without stray text, in Internet Explorer 8.

SVG for Everybody is a JavaScript library for inline SVG fallbacks as well. It’s approach is to start with the most modern possible inline SVG: Inline SVG referenced via <use> pointing to symbols defined in an external file.

<svg role="img" title="CodePen">
  <use xlink:href="spritemap.svg#codepen"></use>
</svg>

If that’s supported, it does nothing. In the case of IE, it Ajax’s for the spritemap and injects it, making it work in modern IE where that normally doesn’t work. In the case of non-SVG supporting browsers, it helps you inject a PNG.

It’s only disadvantage is that the UA test it does isn’t very accurate. You’re probably better off Ajaxing for the SVG and injecting it all the time, which makes for good support and browser caching.


Stuff about Icon Systems

The point of having an icon system is that it makes icons efficient and easy to use. SVG makes for a great icon system. You’ll probably want the fallback for your icon system to still be both efficient and easy to use. Meaning something like the <image> trick would be a pretty bad fallback. It complicates the markup and means every individual icon is a separate request.

You’ll probably want to approach an icon system fallback with one of these methods:

  • Use background images as fallbacks, but have them be a CSS sprite so it still just one request.
  • Use an icon font for a fallback. You’ll need an empty element (suggested markup), but @font-face works a lot further back than SVG does so it can be a good fallback.
  • Use Grunticon, which has you start with an empty element and progressively enhances up to SVG and handles fallbacks (through a JavaScript test). You can also use Grunticon while still starting with inline SVG if you wish, as detailed in this tutorial

Usually any of these fallbacks require some JavaScript to work. If you have a goal of an SVG icon system fallback with no JavaScript (that works in Android 2.2/2.3), here’s a way.

This requires a little extra markup (the <a> in addition to the <use>):

<svg class="icon">
  <a class="svg-status"></a>
  <use xlink:href="#svg-status" />
</svg>

Then in CSS:

.icon a, .icon + a {
  /* styles for every icon */
}
.icon .svg-status,
.icon + .svg-status {
  /* styles for this particular icon */
}

The first selector is for Android, the second for IE. The sibling selector is because old IE won’t treat an unrecognized element as a container.

Background image on an anchor inside an SVG, in Internet Explorer 7

If you don’t like the extra anchor element, and don’t mind losing the fallback when JavaScript is disabled, another strategy is to use JavaScript to inject an element () that both old Android and old IE browsers can style.

The script tests for inline SVG support by seeing if the elements with tag name “use” have the ownerSVGElement property. Use your icon id values (from the <use> element’s xlink:href attribute) as the class names for the new spans, so no extra markup is required.

Conclusion

You can totally do fallbacks for SVG.