DEV Community

Cover image for Building charts in Vue with D3
Jakub Andrzejewski
Jakub Andrzejewski

Posted on

12 1

Building charts in Vue with D3

Creating data visualizations in modern web apps often requires both flexibility and reactivity. Vue.js, a progressive JavaScript framework, is excellent for managing reactive UIs, while D3.js is one of the most powerful libraries for data-driven document manipulation and visualization.

In this guide, we’ll explore how to combine Vue and D3 to build dynamic, interactive charts.

Enjoy!

🤔 Why using D3 for Vue?

Vue handles the DOM reactively using a component-based structure, which is great for app state management. D3, on the other hand, directly manipulates the DOM to render SVG elements based on data. While that might seem like a conflict, with proper structure, they can complement each other well:

  • Vue manages application state and layout.
  • D3 handles drawing and updating the visualization.

Check examples of data visualization with D3.

🟢 Building a basic chart component

Let's create a simple bar chart component.

1.Create a new file BarChart.vue:

<template>
  <svg ref="svg"></svg>
</template>

<script setup>
import { onMounted, ref, watch } from 'vue'
import * as d3 from 'd3'

const props = defineProps({
  data: {
    type: Array,
    required: true
  },
  width: {
    type: Number,
    default: 400
  },
  height: {
    type: Number,
    default: 200
  }
})

const svg = ref(null)

const drawChart = () => {
  const svgEl = d3.select(svg.value)
  svgEl.selectAll('*').remove() // Clear previous renders

  svgEl.attr('width', props.width).attr('height', props.height)

  const x = d3
    .scaleBand()
    .domain(props.data.map((d, i) => i))
    .range([0, props.width])
    .padding(0.1)

  const y = d3
    .scaleLinear()
    .domain([0, d3.max(props.data)])
    .nice()
    .range([props.height, 0])

  svgEl
    .selectAll('rect')
    .data(props.data)
    .join('rect')
    .attr('x', (_, i) => x(i))
    .attr('y', d => y(d))
    .attr('width', x.bandwidth())
    .attr('height', d => props.height - y(d))
    .attr('fill', 'steelblue')
}

onMounted(drawChart)
watch(() => props.data, drawChart)
</script>
Enter fullscreen mode Exit fullscreen mode

Let's stop for a second here to explain each part of the drawChart method:

const drawChart = () => {
  const svgEl = d3.select(svg.value)
  svgEl.selectAll('*').remove() // Clear previous renders
Enter fullscreen mode Exit fullscreen mode
  • d3.select(svg.value): Selects the actual <svg> element from the DOM.
  • selectAll('*').remove(): Clears any previously drawn chart elements to avoid overlap when re-rendering.
  svgEl.attr('width', props.width).attr('height', props.height)
Enter fullscreen mode Exit fullscreen mode
  • Sets the SVG canvas dimensions.
  const x = d3
    .scaleBand()
    .domain(props.data.map((d, i) => i))
    .range([0, props.width])
    .padding(0.1)
Enter fullscreen mode Exit fullscreen mode
  • d3.scaleBand(): Creates a scale for discrete bands (bars).
  • domain: Maps indices of data to bands.
  • range: Spans the scale from 0 to the total width.
  • padding(0.1): Adds spacing between bars.
  const y = d3
    .scaleLinear()
    .domain([0, d3.max(props.data)])
    .nice()
    .range([props.height, 0])
Enter fullscreen mode Exit fullscreen mode
  • d3.scaleLinear(): Linear scale for bar heights.
  • domain: Goes from 0 to the max value in the data.
  • nice(): Rounds the domain to nice round numbers.
  • range: From SVG height (bottom) to 0 (top) because SVG Y-coordinates increase downward.
  svgEl
    .selectAll('rect')
    .data(props.data)
    .join('rect')
    .attr('x', (_, i) => x(i))
    .attr('y', d => y(d))
    .attr('width', x.bandwidth())
    .attr('height', d => props.height - y(d))
    .attr('fill', 'steelblue')
Enter fullscreen mode Exit fullscreen mode
  • .selectAll('rect'): Prepares to bind data to <rect> elements (bars).
  • .data(props.data): Binds the array of values.
  • .join('rect'): Efficiently adds/removes <rect> elements as needed.
  • .attr('x', ...): Sets the x-position for each bar.
  • .attr('y', ...): Sets the y-position (top) of each bar.
  • .attr('width', ...): Sets bar width based on the scale’s bandwidth.
  • .attr('height', ...): Calculates bar height from value.
  • .attr('fill', ...): Sets bar color.

2.Use the BarChart component in your main app file:

<template>
  <div>
    <BarChart :data="chartData" />
  </div>
</template>

<script setup>
import { ref } from 'vue'
import BarChart from './components/BarChart.vue'

const chartData = ref([10, 15, 20, 25, 30])
</script>
Enter fullscreen mode Exit fullscreen mode

The chart should look more or less like this:

D3 Chart

To scale up your Vue and D3 app, make sure to utilize following approaches:

  • Encapsulate D3 logic in composables if complexity grows.
  • Use transitions from D3 for smooth updates.
  • Consider watching window size to make charts responsive.
  • For advanced use cases, look into D3 modules like d3-axis, d3-shape, and d3-brush.

📖 Learn more

If you would like to learn more about Vue, Nuxt, JavaScript or other useful technologies, checkout VueSchool by clicking this link or by clicking the image below:

Vue School Link

It covers most important concepts while building modern Vue or Nuxt applications that can help you in your daily work or side projects 😉

✅ Summary

By combining Vue’s reactive data handling with D3’s powerful visualization tools, you can build highly interactive, performant data visualizations. This approach is perfect for dashboards, real-time analytics, and more.

Take care and see you next time!

And happy coding as always 🖥️

Build seamlessly, securely, and flexibly with MongoDB Atlas. Try free.

Build seamlessly, securely, and flexibly with MongoDB Atlas. Try free.

MongoDB Atlas lets you build and run modern apps in 125+ regions across AWS, Azure, and Google Cloud. Multi-cloud clusters distribute data seamlessly and auto-failover between providers for high availability and flexibility. Start free!

Learn More

Top comments (0)

Gen AI apps are built with MongoDB Atlas

Gen AI apps are built with MongoDB Atlas

MongoDB Atlas is the developer-friendly database for building, scaling, and running gen AI & LLM apps—no separate vector DB needed. Enjoy native vector search, 115+ regions, and flexible document modeling. Build AI faster, all in one place.

Start Free

👋 Kindness is contagious

Dive into this thoughtful piece, beloved in the supportive DEV Community. Coders of every background are invited to share and elevate our collective know-how.

A sincere "thank you" can brighten someone's day—leave your appreciation below!

On DEV, sharing knowledge smooths our journey and tightens our community bonds. Enjoyed this? A quick thank you to the author is hugely appreciated.

Okay