Skip to content
Student Login

Build responsive SVG layouts with react-svg-flexbox

Last updated: August 2018Β πŸ‘‰ livestreamed every last Sunday of the month. Join live or subscribe by email πŸ’Œ

SVG is great. Best way to build scalable graphics on the web. SVG can do everything from simple logos to data visualization and even animation.

The best part is, you can manipulate SVG with both CSS and JavaScript. It's an image that's part of your DOM. 🀯

Look at this animated fire example. Isn't it neat? No animated image required, just some SVG elements and a bit of JavaScript.

SVG Fire πŸ”₯

No wonder then that SVG is the norm when it comes to data visualization and other programmable graphics.

Just one problem: SVG sucks at layout

Absolutely terrible. There's no layout support at all. You get absolute positioning and that's it. Sure, you can position absolutely within absolutely positioned elements, which is sort of relative positioning, but ugh ...

Absolute positioning hell

[caption id="attachment_8574" align="alignnone" width="2792"]

A React + D3 dashboard of scatterplots
A React + D3 dashboard of scatterplots
A React + D3 dashboard of scatterplots[/caption]

Say you're building a small dashboard. Like of different scatterplots looking at a dataset about dog breeds.

Because the data is there, and you can, of course.

You create a scatterplot component. It takes an x and a y position and sizing info. Inside, it places two axes, a caption, and the datapoints.

The <Scatterplot> is absolutely positioned via a translate transformation. That moves an SVG element by a vector from (0, 0), thus rendering at the (x, y) coordinate.

You render each scatterplot like this:

d.weight &&
d.height &&
d.weight[0] &&
d.height[0]
}
xData={d => d.weight[0]}
yData={d => d.height[0]}
xLabel="Weight (lbs)"
yLabel="Height (in)"
title="Dog Breed Height & Weight"
entry={props => (
)}
/>

A lot of arguments, I know. Data, positioning, sizing, functions for interpreting data, a few labels, and a render prop for each datapoint.

Once positioned with a grouping element <g>, the scatterplot can use quasi-relative positioning for its elements.

render() {
return (
{title}
{data.map(d =>
entry({
x: xScale(xData(d)),
y: yScale(yData(d)),
d: d
})
)}
);
}

Position the main container at x and y. Place Heading at (0, -25), datapoints at wherever they go, one axis at (0, 0), and another at left bottom. All relative to the parent container.

The parent container is technically relative to the whole SVG container ... πŸ€”

Absolute or relative, doesn't matter. You're gonna have one hell of a fun time calculating the position of each individual element by hand. D3 scales help, but you still have to think about it.

SVG itself offers zero help.

Want to resize your scatterplots? Here's what happens πŸ‘‡

[caption id="attachment_8575" align="alignnone" width="2660"]

Smaller scatterplots, gotta update positions
Smaller scatterplots, gotta update positions
Smaller scatterplots, gotta update positions[/caption]

Want to resize your browser? Here's what happens πŸ‘‡

[caption id="attachment_8576" align="alignnone" width="1396"]

Resized browser cuts off visualization
Resized browser cuts off visualization
Resized browser cuts off visualization[/caption]

Ugh.

react-svg-flexbox to the rescue

You can fix the layouting problem with react-svg-flexbox. It's a small library, not a lot of stars, but omg so perfect.

Built on top of Facebook's css-layout, which has recently become a part of yoga, it lets you use CSS flexbox to position SVG elements.

Flexbox might be confusing to grok – I look at tutorials any time I use it for anything – but it's way better than doing it yourself. How many engineers worked on browser layout engines over the past two decades?

Wouldn't wanna retrace all those steps yourself πŸ˜…

Wrap our dashboard in a <Flexbox> element and…

\import Flexbox from "react-svg-flexbox";
// ...
// render() etc.
d.weight &&
d.height &&
d.weight[0] &&
d.height[0]
}
xData={d => d.weight[0]}
yData={d => d.height[0]}
xLabel="Weight (lbs)"
yLabel="Height (in)"
title="Dog Breed Height & Weight"
entry={props => (
)}
/>

We take <Flexbox> out of react-svg-flexbox, use flexbox styles to say we want to render in a row that starts at the beginning, and the rest happens on its own.

Note that react-svg-flexbox passes x and y props into our components, so we had to take out manual positioning. Our dashboard now uses up all the space it can πŸ‘‡

[caption id="attachment_8571" align="alignnone" width="2612"]

Scatterplots rendered in a flexbox row
Scatterplots rendered in a flexbox row
Scatterplots rendered in a flexbox row[/caption]

Something's funky with our vertical positioning, but it's an easy fix. Offset y coordinates by a few pixels. Β―_(ツ)_/Β― it's not happening in the official examples, so it must be something we're doing inside those scatterplots.

The axes are still poking outside our width bounds, but this looks better already.

[caption id="attachment_8572" align="alignnone" width="2774"]

Scatterplots with a vertical offset
Scatterplots with a vertical offset
Scatterplots with a vertical offset[/caption]

Even if we make one of them bigger, everything still works. No changes to positioning required! o/

[caption id="attachment_8573" align="alignnone" width="2588"]

One big scatterplot
One big scatterplot
One big scatterplot[/caption]

Responsive layout with react-svg-flexbox

For the biggest win, we add flexWrap: wrap to our <Flexbox> component. Like this πŸ‘‡

You have to specify available width in your styles, otherwise, it doesn't work. That means you should listen to window.onresize and update width accordingly.

Easiest to attach an event listener in componentDidMount. Like this:

class App extends Component {
state = {
width: 1024
};
svgRef = React.createRef();
componentDidMount() {
window.addEventListener("resize", this.updateSize);
}
componentWillUnmount() {
window.removeEventListener("resize", this.updateSize);
}
updateSize = () => {
this.setState({ width: this.svgRef.current.clientWidth });
};
render() {
// ...
}

And your dashboard becomes responsive! Yay

https&#x3A;//twitter.com/Swizec/status/1030372365006327808

See the code, play with examples

You can see a full set of react-svg-flexbox examples on their Storybook.

Code for my dog breed dashboard example is on GitHub here.

You can try it live here.

Fin

Use react-svg-flexbox. Your life will improve. The best thing that's ever happened to me for SVG coding.

Thanks Cody Averett for finding this gem πŸ’Ž

About the Author

Hi, I’m Swizec Teller. I help coders become software engineers.

Story time πŸ‘‡

React+D3 started as a bet in April 2015. A friend wanted to learn React and challenged me to publish a book. A month later React+D3 launched with 79 pages of hard earned knowledge.

In April 2016 it became React+D3 ES6. 117 pages and growing beyond a single big project it was a huge success. I kept going, started live streaming, and publishing videos on YouTube.

In 2017, after 10 months of work, React + D3v4 became the best book I'd ever written. At 249 pages, many examples, and code to play with it was designed like a step-by-step course. But I felt something was missing.

So in late 2018 I rebuilt the entire thing as React for Data Visualization β€” a proper video course. Designed for busy people with real lives like you. Over 8 hours of video material, split into chunks no longer than 5 minutes, a bunch of new chapters, and techniques I discovered along the way.

React for Data Visualization is the best way to learn how to build scalable dataviz components your whole team can understand.

Some of my work has been featured in πŸ‘‡

Created bySwizecwith ❀️