How to Integrate D3 with React or Vue: A Practical Guide
You integrate D3 with React or Vue by importing specific ES modules for data processing directly into your render logic, while wrapping DOM-manipulating D3 calls inside framework lifecycle hooks using element refs to avoid virtual DOM conflicts.
D3 is architected as a collection of tiny, tree-shakable ES modules that separate data mathematics from DOM manipulation. When working with the d3/d3 repository, you can blend D3's computational power with React or Vue's declarative rendering model by respecting this architectural boundary. The official documentation in docs/getting-started.md provides the canonical patterns for this integration, specifically illustrating the React useEffect approach at lines 76‑84.
Architectural Separation: Data vs. DOM
Data Processing Modules
Pure data modules like d3-scale, d3-array, d3-shape, and d3-format operate on primitive values and never touch the DOM. Import these directly into your component and use them to transform data into SVG path strings or scaled coordinates inside your JSX or template.
DOM Manipulation Modules
Modules such as d3-selection, d3-transition, and d3-axis require a real DOM element to function. In React, access these via useRef and useEffect; in Vue, use ref with onMounted or the mounted hook. This pattern ensures D3 manipulates the element only after the framework has mounted it to the DOM, preventing reconciliation conflicts.
Integrating D3 with React
Pure Calculations in JSX
When you only need scales and shapes, import D3 modules directly into your JSX component without side effects. This keeps the component fully declarative and lets React manage the SVG elements.
import * as d3 from "d3";
export default function LinePlot({
data,
width = 640,
height = 400,
marginTop = 20,
marginRight = 20,
marginBottom = 30,
marginLeft = 40,
}) {
const x = d3.scaleLinear([0, data.length - 1], [marginLeft, width - marginRight]);
const y = d3.scaleLinear(d3.extent(data), [height - marginBottom, marginTop]);
const line = d3.line().x((d, i) => x(i)).y(y);
return (
<svg width={width} height={height}>
<path fill="none" stroke="currentColor" strokeWidth="1.5" d={line(data)} />
<g fill="white" stroke="currentColor" strokeWidth="1.5">
{data.map((d, i) => (
<circle key={i} cx={x(i)} cy={y(d)} r="2.5" />
))}
</g>
</svg>
);
}
D3 Selections and Axes with useEffect
For axes or complex transitions, create a ref and invoke D3 inside useEffect after the element mounts. This mirrors the pattern documented in docs/getting-started.md.
import * as d3 from "d3";
import { useRef, useEffect } from "react";
export default function LinePlotWithAxes({ data, width = 640, height = 400 }) {
const gx = useRef(null);
const gy = useRef(null);
const x = d3.scaleLinear([0, data.length - 1], [0, width]);
const y = d3.scaleLinear(d3.extent(data), [height, 0]);
const line = d3.line().x((d, i) => x(i)).y(y);
// Create axes once the refs are attached
useEffect(() => {
d3.select(gx.current).call(d3.axisBottom(x));
d3.select(gy.current).call(d3.axisLeft(y));
}, [x, y]);
return (
<svg width={width} height={height}>
<g ref={gx} transform={`translate(0,${height})`} />
<g ref={gy} />
<path fill="none" stroke="currentColor" d={line(data)} />
</svg>
);
}
Integrating D3 with Vue
Composition API Pattern
Use computed properties for D3 calculations and ref with onMounted for DOM operations. This maintains reactivity while isolating imperative DOM logic.
<template>
<svg :width="width" :height="height">
<path fill="none" stroke="currentColor" :d="linePath" />
</svg>
</template>
<script setup>
import * as d3 from "d3";
import { computed } from "vue";
defineProps({
data: { type: Array, required: true },
width: { default: 640 },
height: { default: 400 },
margin: { default: 20 },
});
const x = computed(() =>
d3.scaleLinear([0, props.data.length - 1], [props.margin, props.width - props.margin])
);
const y = computed(() =>
d3.scaleLinear(d3.extent(props.data), [props.height - props.margin, props.margin])
);
const line = computed(() => d3.line().x((d, i) => x.value(i)).y(y.value));
const linePath = computed(() => line.value(props.data));
</script>
Options API Pattern
Use the mounted hook to access $refs and initialize D3 selections. This ensures the DOM elements exist before D3 attempts to attach axes or transitions.
<template>
<svg :width="width" :height="height">
<g ref="gx" :transform="`translate(0,${height - margin})`" />
<g ref="gy" :transform="`translate(${margin},0)`" />
<path fill="none" stroke="currentColor" :d="linePath" />
</svg>
</template>
<script>
import * as d3 from "d3";
export default {
props: {
data: { type: Array, required: true },
width: { default: 640 },
height: { default: 400 },
margin: { default: 20 },
},
data() {
return { linePath: "" };
},
mounted() {
const x = d3.scaleLinear([0, this.data.length - 1], [this.margin, this.width - this.margin]);
const y = d3.scaleLinear(d3.extent(this.data), [this.height - this.margin, this.margin]);
const line = d3.line().x((d, i) => x(i)).y(y);
this.linePath = line(this.data);
d3.select(this.$refs.gx).call(d3.axisBottom(x));
d3.select(this.$refs.gy).call(d3.axisLeft(y));
},
};
</script>
Key Source Files in d3/d3
Understanding these files helps you leverage the library's modular design when integrating with frameworks:
docs/getting-started.md– Contains the "D3 in React" section (lines 76‑84) demonstrating theuseEffectpattern for DOM manipulation.src/index.js– The ES module entry point that re‑exports all sub‑modules, enabling tree‑shakable imports likeimport {scaleLinear} from "d3".docs/components/deferRender.js– Provides advanced deferred rendering helpers for optimizing D3 updates within component frameworks.docs/what-is-d3.md– Explains the high‑level design intent for framework‑agnostic usage and the separation of data logic from DOM operations.bundle.js– The pre‑built UMD bundle for CDN usage when you prefer script‑tag inclusion over npm.
Summary
- Import specific modules from
src/index.js(e.g.,d3-scale,d3-shape) to enable tree‑shaking and minimize bundle size. - Use D3's data modules directly in render templates for scales, lines, and statistical calculations.
- Isolate DOM-manipulating D3 calls (
d3-selection,d3-axis) inside framework lifecycle hooks using element refs to prevent virtual DOM conflicts. - Reference
docs/getting-started.mdfor canonical integration patterns that align with the d3/d3 repository's official recommendations.
Frequently Asked Questions
Can I use D3's enter-update-exit pattern inside React?
You should not use D3's data-join pattern to create React elements. Instead, use D3 to calculate path data or scales, then render SVG elements declaratively via JSX. Only use D3 selections on refs for behaviors that React cannot handle natively, such as complex axes or transitions.
How do I prevent D3 from conflicting with Vue's reactivity system?
Perform all D3 DOM mutations inside the onMounted or mounted hook after the template ref is bound. Keep reactive data transformations in computed properties using pure D3 math modules. Never let D3 mutate elements that Vue is actively managing through its template.
Is the full D3 bundle required for framework integration?
No. The d3/d3 repository exposes tree-shakable ES modules via src/index.js. Import individual symbols like scaleLinear or line to include only the code you use. Modern bundlers (Vite, Webpack, Rollup) will eliminate unused modules automatically when you import from the modular API.
Where are the official D3 framework integration examples documented?
The primary documentation resides in docs/getting-started.md within the d3/d3 repository, specifically lines 76‑84, which illustrate the React useEffect pattern. The repository also includes docs/what-is-d3.md explaining the design philosophy for framework-agnostic usage and the importance of separating data calculations from DOM manipulation.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →