Home  >   Blog  >   Programming   >   Save HTML <svg> as an Image


Save HTML <svg> as an Image

After playing with interactive data visualization using D3.js and React, I wanted to have a "Download" function so that I can easily save the visualization as an image rather than taking a screenshot of a browser window:

In short, it can be achieved by writing a JavaScript snippet that does the following job:

  1. <svg> to Blob
  2. Blob to Image
  3. Image to <canvas>

Eventually, HTML Canvas enables you to save the content in the form of image file.

For the future reference, let me walk-through the process line-by-line since coming up with a complete snippet wasn't straightforward; there were some "tricky" parts that aren't fully covered by a piece of online resources e.g.:

Assume we have an SVG drawing index.html and its stylesheet e.g., style.css:

<svg xmlns="http://www.w3.org/2000/svg">
  <rect width=64 height=64 />
svg rect {
  fill: blue;

SVG to Blob

The first and most confusing step is to convert SVG into a Blob object represented by XML:

const svg = document.querySelector('svg');

// CSS must be explicitly embedded
const style = createStyleElementFromCSS();
svg.insertBefore(style, svg.firstChild);

const data = (new XMLSerializer()).serializeToString(svg);
const svgBlob = new Blob([data], {
    type: 'image/svg+xml;charset=utf-8'

// remove the temporarily injected CSS

To unlock the conversion, we have to keep the following points in our mind:

  1. Define <svg> with xmlns="http://www.w3.org/2000/svg"
  2. Embed style inside of the <svg> tag

Here, the inserted style element can be dynamically constructed as follows:

const createStyleElementFromCSS = () => {
  // assume index.html loads only one CSS file in <header></header>
  const sheet = document.styleSheets[0];

  const styleRules = [];
  for (let i = 0; i < sheet.cssRules.length; i++)

  const style = document.createElement('style');
  style.type = 'text/css';
  style.appendChild(document.createTextNode(styleRules.join(' ')))

  return style;

Although we defined the style in the separate .css file, it's important for <svg> to explicitly contain the information. Otherwise, a saved image loses all the information about color, font, shape, etc.

Consequently, the svgBlob object has the XML representation of an SVG element below:

<svg xmlns="http://www.w3.org/2000/svg">
  <style type="text/css">
    svg rect { fill: blue; }
  <rect width=64 height=64 />

Blob to Image

Next, loading the Blob object to Image allows the application to treat the original SVG in a more handy way:

// convert the blob object to a dedicated URL
const url = URL.createObjectURL(svgBlob);

// load the SVG blob to a flesh image object
const img = new Image();
img.addEventListener('load', () => {
  // (Next step: Image to Canvas)
img.src = url;

For instance, url denotes a reference to the object as: blob:http://localhost:3000/a1c7704c-09a5-46f5-a102-7bc84d8ecbce

Image to Canvas

Once Image recognizes the Blob URL, we finally draw the image on an HTML Canvas and trigger a download operation.

img.addEventListener('load', () => {
  // draw the image on an ad-hoc canvas
  const bbox = svg.getBBox();

  const canvas = document.createElement('canvas');
  canvas.width = bbox.width;
  canvas.height = bbox.height;

  const context = canvas.getContext('2d');
  context.drawImage(img, 0, 0, bbox.width, bbox.height);


  // trigger a synthetic download operation with a temporary link
  const a = document.createElement('a');
  a.download = 'image.png';
  a.href = canvas.toDataURL();

Notice that the event handler cleanses the environment by removing url and a, an ad-hoc download link. Meanwhile, in case you want to obtain an image in a particular type, canvas.toDataURL() takes an optional parameter like canvas.toDataURL('image/jpeg').

That's it. Now we're capable of extracting an image file from an HTML-coded SVG illustration. My flight emission calculator app already has a Download button for the desired use case.

A set of code snippets below is a complete, compact example of things I explained in this article.


  Support (Thank you!)

  Gift a cup of coffee

Note that, as an Amazon Associate, I earn from qualifying purchases on amazon.ca.

  See also

How Much CO2 Emissions Have Your Flights Made?
Practicing D3 Interactive Data Visualization with Fitbit Activity/Sleep Log
Datavis 2020: A Free Online Course About D3.js & React


Last updated: 2022-09-02

  Author: Takuya Kitazawa

Takuya Kitazawa is a freelance software developer, minimalistic traveler, ultralight hiker & runner, and craft beer enthusiast. While my area of specialty is in data & AI ethics and machine learning productization, I have worked full-stack throughout the career e.g., as a frontend/backend engineer, OSS developer, technical evangelist, solution architect, data scientist, and product manager. You can find what I am doing lately at my "now" page, and your inquiry is always welcome at [email protected], including comments on my blog posts.

  Schedule a call with me

Opinions are my own and do not represent the views of organizations I am/was belonging to.

  Popular articles

Why a Data Science Engineer Becomes a Product Manager
Apache Hivemall at #ODSCEurope, #RecSys2018, and #MbedConnect
Parallel Programming vs. Concurrent Programming