How to dynamically generate SVGs with plain JavaScript and React

Embedded SVGs on websites are a powerful design feature of modern web development. With SVGs you can create sophisticated high-quality interactive data visualizations.

But sometimes, all you want to do is put some user-provided text on a rectangle. As an example, here is a cuboid of dirt with a volume in liters.

Box with volume

I’ll show you three methods how to do that without specialized libraries.

Method 1: Using template literals and data-URI

  1. Create an image in your favourite vector graphics editor (Inkscape) and save it as optimized SVG.
  2. Copy the SVG contents within a JavaScript template literal `...` and replace the text you want to change dynamically. In the following example to: ${amount}ℓ
  3. Convert the string into a data-uri with "data:image/svg+xml;base64," + btoa(source) and use it as image URL.

This method is useful for creating dynamic markers in Leaflet maps:

Method 2: Manipulating SVG in DOM

Instead of rendering a template string to a data URI, you can add the SVG directly in your website and manipulate it using the DOM. In this example, I’ve given the tspan-Element the ID volume.

Method 3: Create SVG using React

React with Typescript has full support for SVG elements. The following demo project renders a timeline visualization of a sequence of nodes. The diagram consists of labels, minor guides, major guides and a line for each node.

020406080100

The blue line connects all start, the red line connects all end. Each node is visualized as a row and has the following properties:

  • min, max - The earliest and latest allowed time
  • start, end - The earliest and latest possible time

Using React function components, it’s very easy to organize the different geometry producing logic. And if you use Typescript, your SVG-code is statically type checked. 👍👍

For example the label is created like this:

1
2
3
4
5
6
7
function TimeLabel({ tx, mins }) {
  return (
    <text x={tx(mins)} y={timeLabelY} textAnchor="middle">
      {mins}
    </text>
  );
}

and the minor guide like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function MinorTimeGuide({ tx, height, mins }) {
  return (
    <line
      x1={tx(mins)}
      y1={headerHeight}
      x2={tx(mins)}
      y2={height}
      stroke={colorMinorTimeGuide}
      strokeWidth="1"
    />
  );
}

Stretching issues

020406080100

At first, I wanted to use some arbitrary coordinate system and then stretch the SVG to fit in a website using preserveAspectRatio="none" (See SVG Pocket Guide). But then I realized that all the text is stretched too. To avoid this, each component needs to calculate the fitted x-coordinates manually. To do that, I’m giving a small function tx(value: number): number as a prop to all components

  • Pocket Guide to Writing SVG - Superb guide about SVG elements and file format.
  • D3 - DOM manipulation with a focus on interactive data visualization, huge ecosystem.
  • Lottie - Render stunning animations, looks great.