Getting started
Installation
// CDN ES Module (Recommended)
import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm";
// npm
npm install d3
import * as d3 from "d3";
// Selective imports (tree-shakeable)
import { select, selectAll } from "d3-selection";
import { scaleLinear } from "d3-scale";
// UMD (legacy)
<script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
Quick Example
// Create bar chart
const data = [4, 8, 15, 16, 23, 42];
d3.select("body")
.selectAll("div")
.data(data)
.join("div")
.style("width", (d) => `${d * 10}px`)
.text((d) => d);
Basic Concepts
| Concept | Description |
|---|---|
| Selections | Query and manipulate DOM |
| Data joins | Bind data to elements |
| Scales | Map data to visual values |
| Shapes | Generate SVG paths |
| Axes | Render reference marks |
| Transitions | Animate changes |
Selections
Selecting Elements
d3.select("div");
// First matching element
d3.selectAll("div");
// All matching elements
d3.select("#id");
// By ID
d3.selectAll(".class");
// By class name
selection.select("span");
// Descendant (propagates data)
selection.selectAll("span");
// All descendants (no data propagation)
Modifying Elements
selection.attr("class", "active");
// Set attribute
selection.attr("width", (d) => d.value);
// With data accessor
selection.style("color", "red");
// Set CSS style
selection.property("checked", true);
// Set DOM property
selection.text("Hello");
// Set text content
selection.html("<b>Bold</b>");
// Set HTML content
selection.classed("active", true);
// Add/remove class
Creating Elements
selection.append("div");
// Append as last child
selection.insert("div", ":first-child");
// Insert before selector
selection.remove();
// Remove from DOM
const svg = d3.create("svg").attr("width", 640).attr("height", 400);
document.body.append(svg.node());
// Create detached element
Data Joins
Modern Join Pattern
svg
.selectAll("circle")
.data(data)
.join("circle")
.attr("cx", (d) => x(d.date))
.attr("cy", (d) => y(d.value))
.attr("r", 5);
// Recommended v7+ pattern
Join with Callbacks
svg
.selectAll("circle")
.data(data)
.join(
(enter) => enter.append("circle").attr("fill", "green"),
(update) => update.attr("fill", "blue"),
(exit) => exit.remove(),
);
// Separate enter/update/exit logic
Key Function
selection.data(data, (d) => d.id);
// Bind by unique key
Legacy Enter/Exit
const circles = svg.selectAll("circle").data(data);
circles.enter().append("circle").attr("r", 5);
// Handle new data
circles.attr("cx", (d) => d.x).attr("cy", (d) => d.y);
// Update existing
circles.exit().remove();
// Remove old elements
Data Binding
selection.datum(value);
// Bind single datum (no join)
selection.data();
// Get bound data array
Scales
Linear Scale
const x = d3.scaleLinear().domain([0, 100]).range([0, 500]);
x(50); // → 250
x.invert(250); // → 50
// Map continuous to continuous
Time Scale
const x = d3
.scaleTime()
.domain([new Date(2020, 0, 1), new Date(2021, 0, 1)])
.range([0, 600]);
// Dates to pixels
Ordinal Scale
const color = d3
.scaleOrdinal()
.domain(["a", "b", "c"])
.range(["red", "green", "blue"]);
// Discrete to discrete
const color = d3.scaleOrdinal(d3.schemeCategory10);
// With color scheme
Band Scale
const x = d3.scaleBand().domain(["A", "B", "C"]).range([0, 300]).padding(0.1);
x("A"); // → start position
x.bandwidth(); // → width of band
// For bar charts
Other Scale Types
| Scale | Description |
|---|---|
scaleLog |
Logarithmic transform |
scalePow |
Power transform |
scaleSqrt |
Square root transform |
scaleQuantile |
Discrete output from quantiles |
scaleQuantize |
Discrete output from thresholds |
scaleThreshold |
Arbitrary breakpoints |
scaleSequential |
Sequential color interpolation |
scaleDiverging |
Diverging color schemes |
Scale Methods
scale.domain([0, 100]);
// Set input domain
scale.range([0, 500]);
// Set output range
scale.clamp(true);
// Clamp values to range
scale.nice();
// Round domain to nice values
scale.invert(value);
// Reverse mapping (continuous scales)
Axes
Creating Axes
const xAxis = d3.axisBottom(xScale);
const yAxis = d3.axisLeft(yScale);
svg.append("g").attr("transform", `translate(0,${height})`).call(xAxis);
svg.append("g").attr("transform", `translate(${marginLeft},0)`).call(yAxis);
// Standard axis setup
Axis Constructors
| Constructor | Description |
|---|---|
d3.axisTop(scale) |
Horizontal, ticks above |
d3.axisRight(scale) |
Vertical, ticks right |
d3.axisBottom(scale) |
Horizontal, ticks below |
d3.axisLeft(scale) |
Vertical, ticks left |
Configuring Axes
xAxis.ticks(10);
// Number of ticks (hint)
xAxis.tickFormat(d3.format(".2f"));
// Custom tick format
xAxis.tickSize(6);
// Tick line length
xAxis.tickPadding(3);
// Space between tick and label
xAxis.tickValues([0, 25, 50, 75, 100]);
// Explicit tick values
Updating Axes
gx.transition().duration(750).call(xAxis);
// Animate axis changes
Shapes
Line Generator
const line = d3
.line()
.x((d) => x(d.date))
.y((d) => y(d.value))
.curve(d3.curveMonotoneX);
svg
.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("d", line);
// Line chart path
Area Generator
const area = d3
.area()
.x((d) => x(d.date))
.y0(height)
.y1((d) => y(d.value));
svg.append("path").datum(data).attr("fill", "steelblue").attr("d", area);
// Area chart path
Arc Generator
const arc = d3.arc().innerRadius(0).outerRadius(100);
svg.append("path").datum({ startAngle: 0, endAngle: Math.PI }).attr("d", arc);
// Pie/donut slice
Pie Layout
const pie = d3
.pie()
.value((d) => d.value)
.sort(null);
svg
.selectAll("path")
.data(pie(data))
.join("path")
.attr("d", arc)
.attr("fill", (d, i) => color(i));
// Pie chart
Curve Types
| Curve | Description |
|---|---|
curveLinear |
Straight line segments |
curveBasis |
Smooth B-spline |
curveCardinal |
Cardinal spline |
curveCatmullRom |
Catmull-Rom spline |
curveMonotoneX |
Monotonic in x |
curveMonotoneY |
Monotonic in y |
curveNatural |
Natural cubic spline |
curveStep |
Step function |
curveStepBefore |
Step before point |
curveStepAfter |
Step after point |
Transitions
Basic Transition
selection.transition().duration(750).attr("r", 10);
// Animate attribute change
Named Transitions
const t = d3.transition().duration(750).ease(d3.easeCubicInOut);
selection.transition(t).attr("fill", "red");
// Reusable transition
Chained Transitions
selection
.transition()
.duration(500)
.attr("r", 10)
.transition()
.attr("fill", "blue");
// Sequential animations
Staggered Transitions
selection
.transition()
.delay((d, i) => i * 10)
.duration(500)
.attr("opacity", 1);
// Delay by index
Easing Functions
| Easing | Variants |
|---|---|
easeLinear |
— |
easeQuad |
In, Out, InOut |
easeCubic |
In, Out, InOut |
easeSin |
In, Out, InOut |
easeExp |
In, Out, InOut |
easeCircle |
In, Out, InOut |
easeElastic |
In, Out, InOut |
easeBounce |
In, Out, InOut |
easeBack |
In, Out, InOut |
Transition Methods
transition.duration(750);
// Animation length (ms)
transition.delay(100);
// Start delay (ms)
transition.ease(d3.easeCubicOut);
// Easing function
transition.on("start", callback);
// Event listener
transition.on("end", callback);
// On completion
Data Loading
CSV Files
const data = await d3.csv("data.csv");
// Load CSV with headers
const data = await d3.csv("data.csv", (d) => ({
date: new Date(d.date),
value: +d.value,
}));
// With type conversion
JSON Files
const data = await d3.json("data.json");
// Load JSON
Other Formats
const data = await d3.tsv("data.tsv");
// Tab-separated values
const text = await d3.text("data.txt");
// Plain text
const xml = await d3.xml("data.xml");
// XML document
const html = await d3.html("page.html");
// HTML document
Type Inference
d3.autoType({ date: "2020-01-01", value: "42" });
// → {date: Date, value: 42}
// Auto-convert strings
Data Utilities
Statistics
d3.min(data, (d) => d.value);
// Minimum value
d3.max(data, (d) => d.value);
// Maximum value
d3.extent(data, (d) => d.value);
// [min, max] array
d3.mean(data, (d) => d.value);
// Average
d3.median(data, (d) => d.value);
// Median value
d3.sum(data, (d) => d.value);
// Sum of values
d3.quantile(values, 0.5);
// Quantile (0.5 = median)
Grouping
d3.group(data, (d) => d.category);
// Map {category => [items]}
d3.groups(data, (d) => d.category);
// Array [[category, [items]], ...]
d3.rollup(
data,
(v) => d3.sum(v, (d) => d.value),
(d) => d.category,
);
// Aggregate by group
d3.index(data, (d) => d.id);
// Map {id => item}
Binning
const bins = d3.bin().domain([0, 100]).thresholds(10)(values);
// Histogram bins
const bins = d3
.bin()
.value((d) => d.value)
.thresholds(d3.thresholdScott)(data);
// With accessor and threshold function
Arrays
d3.range(10);
// [0, 1, 2, ..., 9]
d3.range(5, 10);
// [5, 6, 7, 8, 9]
d3.range(0, 1, 0.2);
// [0, 0.2, 0.4, 0.6, 0.8]
d3.shuffle(array);
// Random permutation
d3.ticks(0, 10, 5);
// Nice tick values
SVG Helpers
SVG Creation
const svg = d3
.create("svg")
.attr("width", 640)
.attr("height", 400)
.attr("viewBox", [0, 0, 640, 400]);
document.body.append(svg.node());
// Create SVG element
Transforms
selection.attr("transform", `translate(${x},${y})`);
// Move element
selection.attr("transform", `rotate(${angle})`);
// Rotate (degrees)
selection.attr("transform", `scale(${k})`);
// Scale element
selection.attr("transform", `translate(${x},${y}) rotate(${angle})`);
// Combined transforms
Paths
const path = d3.path();
path.moveTo(x, y);
path.lineTo(x, y);
path.arc(x, y, radius, startAngle, endAngle);
path.closePath();
const d = path.toString();
// Manual path construction
Events
Event Handlers
selection.on("click", function (event, d) {
// this = DOM element
// event = native event
// d = bound datum
});
// Standard event handler
selection.on("click", (event, d) => {
// Arrow function (no this binding)
});
// Arrow function style
Common Events
selection.on("mouseover", handler);
// Mouse enters
selection.on("mouseout", handler);
// Mouse leaves
selection.on("mousemove", handler);
// Mouse moves
selection.on("click", handler);
// Mouse click
selection.on("dblclick", handler);
// Double click
Removing Handlers
selection.on("click", null);
// Remove event listener
Event Object
function handler(event, d) {
event.target; // Element
event.currentTarget; // Element with listener
event.pageX; // Mouse X
event.pageY; // Mouse Y
event.preventDefault();
event.stopPropagation();
}
Color Schemes
Categorical Schemes
d3.schemeCategory10;
// 10 colors
d3.schemeTableau10;
// Tableau 10 colors
d3.schemeSet1;
d3.schemeSet2;
d3.schemeSet3;
// ColorBrewer sets
Sequential Schemes
d3.interpolateBlues;
d3.interpolateGreens;
d3.interpolateReds;
// Single hue
d3.interpolateViridis;
d3.interpolatePlasma;
d3.interpolateInferno;
// Perceptually uniform
Diverging Schemes
d3.interpolateRdBu;
d3.interpolatePiYG;
d3.interpolateBrBG;
// Diverging color schemes
Using Schemes
const color = d3.scaleOrdinal(d3.schemeCategory10);
// Categorical scale
const color = d3.scaleSequential(d3.interpolateViridis).domain([0, 100]);
// Sequential scale
Gotchas
Data Propagation
⚠️ Data does NOT propagate through .selectAll() — Use .select() for single-child selections to propagate parent data.
// WRONG - data is lost
parent.selectAll("g").selectAll("circle");
// RIGHT - data propagates
parent.selectAll("g").select("circle");
API Changes (v3 → v7)
⚠️ D3 v7 has breaking changes from v3:
d3.scale.linear()→d3.scaleLinear().enter().append()→.join()- Removed
d3.layout.*— use separate packages
Selection Types
⚠️ Selections are NOT DOM nodes — Use .node() to access the underlying element:
const svg = d3.select("svg");
svg.getAttribute("width"); // ❌ Undefined
svg.node().getAttribute("width"); // ✅ Works
Method Chaining Returns
⚠️ Different methods return different types:
.data()→ update selection.enter()→ enter selection.transition()→ transition (not selection)
Transition Limitations
⚠️ Transitions only interpolate attrs/styles, not data — Rebind data first:
// WRONG
selection.transition().data(newData);
// RIGHT
selection.data(newData).transition();
Scale Edge Cases
⚠️ Scales don't handle null/NaN — Filter invalid data before scaling:
data.filter((d) => d.value != null);
Also see
- D3.js Official Documentation — Comprehensive API reference
- Observable D3 Gallery — Interactive examples
- d3-selection API — Selection methods
- d3-scale API — Scale functions
- d3-shape API — Shape generators
- d3-axis API — Axis rendering
- d3-transition API — Animated transitions
- d3-array API — Data manipulation utilities
- d3-fetch API — Data loading functions