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>
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
-
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)
- Sets the SVG canvas dimensions.
const x = d3
.scaleBand()
.domain(props.data.map((d, i) => i))
.range([0, props.width])
.padding(0.1)
-
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])
-
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')
-
.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>
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 🖥️