Generating SVG With React

Quick Summary

React is one of today’s most popular ways to create a component-based UI. It helps to organize an application into small, human-digestible chunks. With its “re-render the whole world” approach, you can avoid any complex internal interactions between small components, while your application continues to be blazingly fast due to the DOM-diffing that React does under the hood (i.e. updating only the parts of the DOM that need to be updated).
But can we apply the same techniques to web graphics — SVG in particular? Yes! I don’t know about you, but for me SVG code becomes messy pretty fast. Trying to grasp what’s wrong with a graph or visualization just by looking at SVG generator templates (or the SVG source itself) is often overwhelming, and attempts to maintain internal structure or separation of concerns are often complex and tedious.

Table of Contents

Members support Smashing

Wonderful, friendly people who keep this lil' site alive — and get smarter every day.

React is one of today’s most popular ways to create a component-based UI. It helps to organize an application into small, human-digestible chunks. With its “re-render the whole world” approach, you can avoid any complex internal interactions between small components, while your application continues to be blazingly fast due to the DOM-diffing that React does under the hood (i.e. updating only the parts of the DOM that need to be updated). But can we apply the same techniques to web graphics — SVG in particular? Yes!

I don’t know about you, but for me SVG code becomes messy pretty fast. Trying to grasp what’s wrong with a graph or visualization just by looking at SVG generator templates (or the SVG source itself) is often overwhelming, and attempts to maintain internal structure or separation of concerns are often complex and tedious.

“You must unlearn what you have learned!” Meet the brand new episode of SmashingConf San Francisco with smart front-end tricks and UX techniques. Featuring Yiying Lu, Aarron Draplin, Smashing Yoda, and many others. Tickets now on sale. April 17-18.

This is the most interesting part: the opportunity to stop and think about what our graph consists of. This is one of the best processes when developing with React: We can think about high-level components first and split them into more granular ones later.

Two axes and a graph body — looks logical to me. Of course, the code will not work. This is just an attempt to shape an initial API of our graph: We haven’t implemented child components yet, and we have some undefined variables such as width and height. Let’s finish this step by step.

We need to set some dimensions for our graph. We could hardcode them, but better to use defaultProps:

Just look: We can read that like plain English. Anyone should be able to understand what is happening here. Now, when our parent component looks ready, it’s time to switch focus to the children.

Axes should just return lines, nothing complex there. According to the SVG specification, to create a line, we need to pass four coordinates: x1, y1, x2, y2. And keep in mind that axes may be vertical or horizontal and should respect the initial position passed through props:

Returning null in this case will force React to render a noscript tag. We can achieve the same “empty” result by using return <g />, which will return an empty SVG group.

Groups in SVG are something like div elements in HTML, very useful when your component should return more than one node. By default, this will not work in JSX (only the last node will be returned), so we’ll wrap everything in a <g> element to avoid this.

The next step is to remove the stub and create a fully functional graph body. To draw a graph line, we will use a path. This requires us to pass a specially crafted string as a d parameter. Crafting this string is easy; it consists of two parts: an initial Moveto command and a bunch of Lineto commands to draw the graph itself:

Moveto will be our starting point: M ${this.props.x} ${this.props.y}. This will move our brush to the initial coordinates. Then, we will connect each data point together with the L x y command.

However, we can’t pass x and y just as we get them from the data set. We need to sum them with a starting point for x and subtract from the starting point for y, because the y axis in SVG goes from top to bottom.

I’ve also multiplied the coordinates by a constant just to make the graph prettier.

Adding support for multiple data sets is an easy task for us, thanks to React’s top-to-bottom data flow approach. (View large version)

So, we’re ready to ship! But let’s say that just before that, our data changes. Suppose the data science department extends our data set by another array and asks us to create a way to switch data on the fly.

Adding support for multiple data sets is an easy task for us, thanks to React’s top-to-bottom data flow approach. We just need to change the data that we are passing to the Graph component dynamically; React will do the re-rendering for us.

Now our data miners are happy; they can play with data sets on the fly!

But tomorrow comes, and now they want to be able to download rendered graphs to work with offline. Previously, that would mean a lot of work, but React has no real DOM dependency, so you can render it on a server easily.

We start by creating a simple Express app that handles incoming requests for SVG graphs (svg_server.js):

Now, our data science department fully loves us and we can finally go home. If you missed anything, you can find the whole example in the repository.

I hope this tutorial shows you that, from React’s perspective, there is no difference at all in what to render. You can leverage all of the ideas that shape your HTML in SVG, and have small, understandable components that anyone can easily change without breaking any external dependencies.

But should you create your own graph systems from scratch? No, plenty of great solutions can be extended easily to work with React (and even completed integrations — react-d3, for example). My hope is that, in making this graph, you’ve come to understand how these integrations work under the hood.

A small warning before wrapping up. Keep in mind that React does not support all SVG elements right now (there are some limitations and missing pieces), but you’ll probably find that it has what you need for the most common scenarios. For the less common ones, React provides a way to set the innerHTML of an element via dangerouslySetInnerHTML, which can help you work around any missing SVG elements you might require. Also, looks like many of these issues will be fixed in the next React version.