Using SVG in backgrounds with PNG fallback

Warning This article was written over six months ago, and may contain outdated information.

My last post was about using SVG values for the background-image property, and I pointed out one big problem with the technique:

The drawback of this is that it’s not ready for use just yet — browsers that don’t support SVG in background-image will not provide any fallback, even if you supply another background-image value; so in non-supporting browsers, no image at all will be displayed.

This was annoying me a little, and I couldn’t find any workarounds that didn’t use JavaScript. However, after a bit of head-scratching I’ve come up with a way to get around it.

In order to have browsers which don’t support SVG display a PNG image instead, you should use this code:

E {
background-image:  url('image.png');
background-image:  none,url('image.svg'), url('image.png');
background-size: 100% 100%;

Browsers which support SVG and multiple backgrounds will show the SVG; browsers which don’t support SVG will fall back to the PNG in the background-image stack. As far as I know, all browsers which support SVG also support multiple backgrounds; any which don’t (i.e. IE) will fall back to the PNG in the first background-image declaration.

Update: Thanks to a comment I have amended this technique. IE was not falling back to the first background-image as it should have done, but I got around this by adding a none value in the declaration. This invalidates the rule in IE and forces the fallback to the previous declaration.

You can see a demonstration of it here: Background SVG with PNG fallback — demo.

This technique is not without its own caveats, however. First and foremost, the SVG must not have a transparent background, or else the PNG in the background layer below it will probably show through. Also, depending on how fast the SVG file loads, the PNG file may show up first and then be covered over.

Finally, if you’re using the same background image at different sizes and either your browser doesn’t support background-size or you don’t want automatically resized PNGs, you’ll have to use the rule for each instance:

E {
background-image:  url('image-small.png');
background-image:  none,url('image.svg'), url('image-small.png');
background-size: 100% 100%;

F {
background-image:  url('image-large.png');
background-image:  none,url('image.svg'), url('image-large.png');
background-size: 100% 100%;

And of course, if you’re creating multiple PNG images anyway, you may feel there’s not a lot of point in using SVG images as well. I’ll be the first to admit that this isn’t a completely bulletproof technique, but I offer it in the hope that in certain circumstances it could be useful.

16 comments on
“Using SVG in backgrounds with PNG fallback”

  1. Why can’t you do this:

    E {
    background-image: url(‘image.png’);
    background-image: url(‘image.svg’), url(‘image_that_doesnt_exist.png’);
    background-size: 100% 100%;

  2. It’s a good idea, Martin, but it doesn’t work. The fallback to the previous background-image declaration would only happen if the browser didn’t understand the multiple syntax. However, some browsers — notably Firefox — do understand the syntax, so the fallback doesn’t occur, but can’t locate the file for the background-image, so treat it as none.

  3. Just checked this on Windows 7, Internet Explorer 8… all squares blank. And the background on Firefox 3.6.6 does not scale to browser width, so the Safari 5.x and Chrome 5.x works. Guess this trick is obsolete now.

    BerggreekDK [July 1st, 2010, 18:21]

  4. Hmm, it worked in IE8 when I last checked; I’ll take another look later and confirm. Thanks for the feedback.

    Update: You were right; IE was not falling back as expected. I’ve updated the technique so it works as expected now. Thanks a lot for spotting this.

  5. The drawback of this method is obviously that if the browser supports the CSS 3 background-image property, the browser will fetch both images and lay them over each other (the SVG will be on the first layer and should cover the PNG, so that it becomes invisible). This also means that without optimisation (that is browser detects that the SVG covers the entire area and ignores all other layers) the browser will have to scale both images.

  6. You’re right, Matthias, it’s certainly not a method without any drawbacks; I’d hesitate to use it on a production site because it has so many limitations.

  7. Chrome breaks it. It causes both the SVG and the PNG to display simultaneously.

    Bryan Elliott [September 21st, 2010, 19:51]

  8. I don’t quite get what you mean, Bryan; it’s showing the two side-by-side? Or one appears under the other? I’m looking at it in Chrome now and it seems fine.

  9. Hey Peter, like the idea, why not just make the second background image a 1x1px transparent png, seem to work fine for me. Also i base 64’ed the png in as its such a tiny file its now worth the extra request.

  10. This solution does not work on Android 2.3 :(

    Somebody know a solution for that?

  11. Hello Peter. Just wanted to say thanks for posting this method. With the rise of high resolution screens like Apple’s retina displays, I was looking into using SVG with some sort of image fallback. This CSS method did exactly what I needed to do :)

  12. I created a similar technique but using CSS gradient to discriminate SVG support. Instead of none I use an invisible gradient. That solves the problem with Anddroid 2.X browsers. More details at

  13. Use this instead:
    img {
    background: #ffffff url(image.png);
    background: rgba(255,255,255, 1.0) url(image.svg);

    Note the ‘rgba’ instead of ‘none’.
    This way you will get a svg+white background with rgba and since IE<=8 does not support rgba a fallback to png+white background the old way.

  14. rgba works IE7 (use png), FF, Chrome

  15. If you use conditionals on the html tag you can do the following which eliminates any worries about multiple backgrounds or load times:

    E { background: url(img/header-sprite.svg);}
    .ie8 E { background: url(img/header-sprite.png);}

  16. Thanks, it worked!