Dynamic Data Visualizations With Vue.js and D3

Petr Mitev
The Startup
Published in
7 min readJun 27, 2019

Background

Source Code

In 2019, there’s almost no reason to give the Vue.js backstory before diving in; its unprecedented popularity has already made it a “household” name amongst web developers, and not without good reason. Vue does a lot of great things under the hood to help create web apps, but its biggest claim to fame might not be the “core” technology itself, but rather the developer experience it creates.

Legibility and accessibility is a key part of that developer experience. Being able to read and understand code quicker because of the Vue syntax and modular structure makes the work more accessible and reduces frustration. Because of that strength, Vue is an ideal framework for combining with a rich/complex library. Taking advantage of the legibility and modularity of Vue, one can simplify the interface with such a library by parsing it up into smaller, reusable pieces and wrapping opinionated syntax with the idiomatic Vue style.

In our case, that rich library is D3. D3 has been one of the foremost data visualization library for JavaScript in recent years, with development still going strong under creator Mike Bostock’s vision. What sets D3 apart from many other libraries is the ability for infinite customization. Its low-level API provides direct control over the native SVG elements, but it also comes with the cost of a high learning curve. Because of that paradigm, we’re going to bring D3 and Vue together — using Vue’s dynamic data-binding, legible syntax, and modular structure to bring out the best in D3.

The animated bar chart we’re going to build

The Project

We’re going to use D3 and Vue to build a basic bar chart component. There are a ton of great guides out there for doing this in vanilla D3, but we’re going to focus on trying to write idiomatic Vue rather than idiomatic D3. The full code for this project can be found on CodeSandbox, and will be referenced throughout the rest of the guide.

Set Up

First, we’ll need to install the dependencies for our project. We could simply install and use all of D3 with npm i d3 but that can slow down our application with all of the modules we’re not using, especially the heavier d3-geo module. Instead, we’ll only install the D3 modules we need.

The dependencies to install in your project

The next step will be to create our BarChart.vue file, and provision our App.vue or other parent component to render the bar chart. For this guide, the data will be defined in the parent App.vue file for convenience, but in other applications, you may want to handle getting/setting data through Vuex, especially if you’re loading it from a file or other external source.

Project File Structure

App Component

We’re also going to preemptively pass some props to our bar chart component. The first prop will be the data array itself, but the other two are going to be how we define the data model in D3. Instead of writing a one-off chart every time we work with a different data-set, we’re going to try and keep things re-usable by passing in the names of the properties in the data array that we want D3 to work with.

The App.vue file hosting our bar chart component

Bar Chart Component Definition

Creating the bar chart component begins with importing our D3 modules, and defining the props we passed in the App.vue component.

Defining the bar chart component

After that, we scaffold our Vue template to receive the SVG elements. Notice that we’re adding individual <svg> <g> and <rect> elements rather than with the familiar D3 function chains. This will allow us to dynamically bind these elements to data within our Vue component, and take advantage of Vue’s reactivity. The other element to highlight here is that we’re going to dynamically compute our width and height attributes of the <svg> element — this is done to keep the content legible across all possible screen sizes and eliminate of screen rendering.

Template scaffold

Watched Properties

We’re only going to use one watched property here, and that will be for our computed width. Normally we’d use a computed property for something like this, but we’ll need to pull information from the DOM after elements have been rendered, and computed properties throw errors when trying to do so. For this property, we’ll get the width of the parent <div> of the bar chart, and set our width to be 3/4 of that dimension to give some buffer around the edges.

Reactive width property

Computed Properties

These properties are going to be one of the biggest parts of our component. This is where we can break up the D3 logic into more digestible bits, and update/consume it as changes happen in real time. The basic function of the properties is commented in the code below, but for a more in-depth understanding of what the D3 functions are doing, please reference the D3 source documentation or D3 In Depth.

Computed properties

Creating the Bars

Now that we have our scaling functions and other computed properties, we can start adding the bars in the template. We’ll do this through a v-for directive — looping through the array of data items, and dynamically assigning their properties to the <rect> element in the DOM. Notice that we’re accessing the individual items through the xKey and yKey props we passed in earlier. This will allow us to reuse the same component irregardless of whether the next project’s data-set contains the keys “name” and “amount”.

The other key thing to notice here is that these bars won’t yet render (redrawToggle), and won’t have a height and y placement when they do ( y and height). We’re doing this because we want to animate the chart, which requires adding those properties with a D3 transition after the initial render.

Creating the bars

Animating the Chart

In order to animate the chart, well create some methods to wrap and modularize our transition logic. The draw animation consists of assigning the correct height and y values to the <rect> elements already rendered on the page with a delay — creating an effect of the bars “growing” from the x-axis.

In the mounted hook, we’ll add a listener for window resize events which will trigger the draw animation, and set the <svg> size to be the relative to the new screen/window dimension. Instead of firing this immediately, we’ll wait 300ms to be a little more “sure” that the user is done resizing their window.

Implementing the animation methods and dynamic resizing
Methods for animating the drawing of the chart, and redrawing on window resize

Styling

Styling is entirely up to you, and with Vue’s scoped style syntax and D3’s low-level API, you can customize to your heart’s content. You can also mix CSS animations with D3 transitions and Vue transitions/animations for even more dynamic/reactive effects. For this project, the only real styling was focused on the <div> element which determines the sizing and placement of the <svg> inside of it.

Summary

Vue has allowed us to organize and structure our component in a clean and reusable way. It has also created a more legible code base, especially for beginner coders or any developer who is not deeply familiar with D3’s API or data processing workflows. It takes some practice to get used to the Vue + D3 style, especially as most of the online examples are in the traditional D3 syntax, but there are benefits for the patient coder. A dynamic, and reusable piece of data visualization is a powerful thing — and it can make all the difference in a data-driven world.

Source Code

--

--