D3 18,089 's latest release is 7.9.0, from 12 March 2024. The quiet since means a finished API, not abandonment: the d3/d3 repository (ISC, about 113,800 stars) still takes commits, and the modules ship fixes on their own schedule. Node 18 or later can check this:
const get = name => fetch(`https://registry.npmjs.org/${name}`).then(r => r.json());
const d3 = await get('d3'), v = d3['dist-tags'].latest;
console.log(`d3@${v} published ${d3.time[v].slice(0, 10)}; it re-exports:`);
const mods = await Promise.all(Object.keys(d3.versions[v].dependencies).map(async name => {
const p = await get(name), m = p['dist-tags'].latest;
return ` ${p.time[m].slice(0, 10)} ${name}@${m}`;
}));
console.log(mods.sort().reverse().slice(0, 3).join('\n'), `\n ... ${mods.length} modules`);Output
d3@7.9.0 published 2024-03-12; it re-exports: 2026-01-14 d3-format@3.1.2 2024-03-12 d3-scale-chromatic@3.1.0 2024-03-12 d3-geo@3.1.1 ... 30 modules
Because d3 asks for "d3-format": "3", npm 2,036 install d3 now resolves d3-format 3.1.2 (January 2026, with fixes to formatPrefix and to the s format for NaN and infinity), while the CDN's d3.min.js was bundled in March 2024 with the older one, so a bundled app and a <script> page can format an edge case differently.
<!doctype html>
<style>
body { margin: 0; padding: 8px; background: #fafaf7; font: 12px system-ui, sans-serif; color: #263238; }
svg { width: 100%; max-width: 600px; display: block; }
</style>
<script src="https://cdn.jsdelivr.net/npm/d3@7.9.0/dist/d3.min.js"></script>
<p id="loaded"></p>
<svg viewBox="0 0 600 200" font-size="11"></svg>
<script>
// d3.version comes from the script the page actually loaded (no registry fetch needed)
d3.select('#loaded').html(`This page loaded <b>d3 ${d3.version}</b>; the latest release is 7.9.0 (12 March 2024).`);
const releases = [
['1.0', '2011-02-18'], ['2.0', '2011-08-23'], ['3.0', '2012-12-21'], ['4.0', '2016-06-28'],
['5.0', '2018-03-22'], ['6.0', '2020-08-26'], ['7.0', '2021-06-11'], ['7.9.0', '2024-03-12'],
].map(([v, date]) => ({ v, date: new Date(date) }));
// Modules ship fixes on their own schedule
const moduleFix = { v: 'd3-format 3.1.2', date: new Date('2026-01-15') };
const x = d3.scaleUtc([new Date('2010-06-01'), new Date('2026-12-31')], [20, 580]);
const svg = d3.select('svg');
svg.append('g').attr('transform', 'translate(0,150)').call(d3.axisBottom(x).ticks(d3.utcYear.every(2)));
svg.append('line').attr('x1', 20).attr('x2', 580).attr('y1', 110).attr('y2', 110).attr('stroke', '#b0bec5');
const mark = svg.selectAll('g.r').data(releases).join('g').attr('class', 'r')
.attr('transform', (d, i) => `translate(${x(d.date)},110)`);
mark.append('circle').attr('r', 6).attr('fill', d => d.v === d3.version ? '#e09a10' : '#1f5f8b');
mark.append('text').attr('y', (d, i) => i % 2 ? 24 : -12).attr('text-anchor', 'middle')
.attr('font-weight', d => d.v === d3.version ? 'bold' : null).text(d => `v${d.v}`);
const fix = svg.append('g').attr('transform', `translate(${x(moduleFix.date)},70)`);
fix.append('path').attr('d', 'M0,-6L6,0L0,6L-6,0Z').attr('fill', '#3f7d3a');
fix.append('text').attr('x', -8).attr('dy', '0.35em').attr('text-anchor', 'end').text(moduleFix.v + ' (January 2026)');
// The quiet stretch since 7.9.0: a finished API, not abandonment
svg.append('rect').attr('x', x(releases.at(-1).date)).attr('width', x(new Date('2026-09-28')) - x(releases.at(-1).date))
.attr('y', 104).attr('height', 12).attr('fill', '#e09a10').attr('opacity', 0.25).lower();
svg.append('text').attr('x', x(new Date('2025-06-01'))).attr('y', 132).attr('text-anchor', 'middle')
.attr('fill', '#8d6e63').text('no new d3 release');
</script>