If you follow me on twitter (I’m @OliverCaldwell, if you don’t) you will have noticed that I’ve been tweeting about D3 and React a lot recently. More specifically, how to use both together in a pleasant yet efficient way. After a few weeks of thought and a couple of potential solutions actually being built and used, I settled on what I think is the ideal way to use DOM mutating JavaScript (like D3) from within React.

Many solutions involve stepping out of the React tree for that specific component, which does work, but leaves you with a little island of mutable DOM, festering away inside your tree. It just doesn’t feel quite right to me, my solution allows you to even use React developer tools with the SVG generated by D3.

TL;DR (although I’d quite like it if you read the rest!): Use react-faux-dom to seamlessly blend D3 and other libraries into your React component tree.

The problem

D3 (data driven documents), as you probably know, is a JavaScript library that helps you build visualisations or anything else for that matter. It’s actually very general purpose since you can render SVG or regular DOM elements with it.

It works by mutating the DOM element provided to it, usually a root node you placed into your HTML. You call .append('p') and it inserts a <p></p> as a child of the root node you selected.

React on the other hand has you build your application through one big call stack to various render methods. You create a tree of objects that represents your application and its state then React works out what DOM elements it should mutate, add or remove on your behalf. This process is called reconciliation.

If you give an element React created to D3 and say “hey, D3, set the width to 100, thanks” it’ll happily oblige. Then React will notice and get pretty upset that you went behind its back and messed with its perfectly reconciled DOM. This isn’t great, as you can imagine.

So, the way we usually get around these problems is to tell React that from this component and below, it shouldn’t interfere, we will manage the DOM below this component manually. That’s where things like react-d3-wrap come in, they define a component you can inherit from that pulls your code out of the React tree and lets D3 do its thing, React skips happily past this part of the DOM.

But what if we want to keep the React tree and use D3? What if we don’t want to have part of our DOM not managed by our benevolent DOM reconciling God?

First (deprecated) attempt: d3-react

d3-react is essentially a plugin for D3 (injected into the D3 prototype) that gives you React methods within the D3 API. What this means is you need to use .prop() instead of .attr() and then you can call .toReact() when you’re done and return that result from your render function.

You create a DOM node and pass it to D3 on each render. It mutates this detached DOM and then gets converted to React elements when you’re done.

This has multiple problems, first and foremost: You can’t use the full D3 API or existing components! You have to use .prop, you can’t use .classed or .style for example. This is just unacceptable, any good solution needs to also work with existing components or with minimal tweaking, not a full rewrite.

The other problem (or one of them, anyway) is that you’re building and mutating a full real DOM tree on every render and then throwing it away. This is pretty inefficient, the DOM isn’t exactly light weight, that’s why we’re using React in the first place! (well, one of the reasons)

It works, you don’t need to opt out of the React render tree, but it’s not great. So what’s the next step after this?

We make our own DOM

Yeah. I thought it was a stupid idea when it first came to me, but the more I thought about it the more it made sense. A fake DOM that supported enough methods to work with D3, but no more. If it needs to work with more libraries in the future you just need to add the missing DOM methods, easy.

And so, react-faux-dom was born. A fake DOM with enough methods to trick D3, including a selector engine and partial support for addEventListener etc. (addEventListener sets the appropriate prop value, so if you add two, it will overwrite the first, I may improve this later)

You can use the full D3 API (no special React methods), inspect it with React developer tools, have efficient D3 components without carefully placed .enter() / .exit() calls, use React animation techniques instead of D3s (good and bad, both approaches work well) as well as render on the server side. Yeah, buzzword time, this will allow you to have isomorphic charts.

drops mic

I copied one of mbostock’s charts (the awesome creator of D3) and rendered it through react-faux-dom easily. Nothing needed to change, it just works. I’ve also converted a complex chart over to this at work from react-d3-wrap, it was pretty easy to do and now it’s far more efficient. That chart even has dynamic resizing and hover tooltip interaction!

A side effect of migrating that chart over was that I could make use of this.setState({...}) to re-render my chart upon mouse interaction or window resize. It’s now much cleaner and easier to follow in my opinion.

The best thing I can compare this to is a lightweight and simple version of jsdom that targets React elements and only supports the minimum required DOM API to work with D3 (right now).

d3-react-sparkline is a small React component I built at work (I work at Qubit, it’s awesome here) originally using my first approach but migrated to faux DOM. It serves as a good example, it should be familiar to React users and D3 users alike. All concepts remain the same, react-faux-dom is just the glue in the middle.

Other implications

Firstly, ISOMORPHIC CHARTS! A concept I find so awesome I thought it was worth mentioning again, in bold all caps. No more “we’re just fetching the data for this chart, brb”, send that rendered SVG straight down to the browser and then have React pick up where it left off on the server with the data as it changes over time. Amazing!

It’s not just charts though, I see a lot of function calls happening in amongst JSX to turn complex data into complex elements. Those elements don’t have to be bars in a chart, it could just be a <ul> with other nested <ul> tags inside. These function calls can be hard to follow when compared to D3’s declarative chaining syntax.

Why not use D3 as your JSX? D3 is an excellent (if not the best) tool for turning data into DOM.

function render() {
  var list = ReactFauxDOM.createElement('ul')

  d3.select(list)
    .selectAll('li')
    .data(this.props.data) // 1, 2, 3...
    .enter()
    .append('li')
    .text(function (d) {
      return d
    })

  return list.toReact()
}

This results in a list containing each value passed through props, each with a unique key automatically assigned by index if you didn’t provide one. This automatic key assignment allows you to use existing D3 components without getting warnings from React, you can assign your own keys though if you feel it’ll optimise the reconciliation.

We’re taking the D3 data driven mindset, but running it within the stateless simple world of React seamlessly, no DOM required.

I’m using this in production right now, this is not just a little script knocked together in a day (although it was), it’s built to solve an existing problem in a nice way. Let me know if you find any any issues with it, raise a bug, even fix it if you can. It’s ready to use on real projects.

I hope many of you find this post and project useful, I’ve tried to explain “why” and not just “what” it is. Please feel free to give me your feedback, I’m very interested in hearing what others working on similar problems think about this.

Thanks for reading.