{"id":15760549,"url":"https://github.com/joelburget/d4","last_synced_at":"2025-04-05T08:06:20.829Z","repository":{"id":52115424,"uuid":"55103836","full_name":"joelburget/d4","owner":"joelburget","description":"Data-Driven Declarative Documents","archived":false,"fork":false,"pushed_at":"2016-07-18T18:19:13.000Z","size":4283,"stargazers_count":818,"open_issues_count":4,"forks_count":20,"subscribers_count":29,"default_branch":"master","last_synced_at":"2025-03-29T07:05:52.729Z","etag":null,"topics":["d3","data-visualization","react"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/joelburget.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-03-30T23:04:16.000Z","updated_at":"2025-03-21T20:48:00.000Z","dependencies_parsed_at":"2022-09-08T06:40:19.169Z","dependency_job_id":null,"html_url":"https://github.com/joelburget/d4","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joelburget%2Fd4","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joelburget%2Fd4/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joelburget%2Fd4/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joelburget%2Fd4/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/joelburget","download_url":"https://codeload.github.com/joelburget/d4/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247305933,"owners_count":20917208,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["d3","data-visualization","react"],"created_at":"2024-10-04T10:58:44.822Z","updated_at":"2025-04-05T08:06:20.794Z","avatar_url":"https://github.com/joelburget.png","language":"JavaScript","readme":"# d4 -- **Declarative** Data-Driven Documents\n\n## What is it?\n\nd4 is an experiment in using React to produce data-driven documents (ala d3)\nthat are performant and understandable. This is *not* a library, but rather a\ndemonstration that it's possible (and preferable) to use React instead of the\ncore of d3.\n\n## Why?\n\nd3 can produce fantastic results. Look no further than [Mike Bostock's\nblocks](https://bl.ocks.org/mbostock) for examples. Unfortunately, I\nalways find d3 code surprisingly difficult to understand and extend, in the\nsame way I used to find code difficult to approach before React encouraged a\ndeclarative style. By using React (which can render SVGs, no problem) for\ndata-driven documents, we can improve comprehension and performance and use\ntools from the React ecosystem.\n\n## How does it work?\n\nWe replace the core d3 interaction of [Enter, Update, Exit](https://medium.com/@c_behrens/enter-update-exit-6cafc6014c36#.yty2g8g0e) with, well, `render`. Let's first see an example.\n\n### d3\n\n```javascript\nvar svg = d3.select(\"body\").append(\"svg\")\n    .attr(\"width\", width)\n    .attr(\"height\", height);\n\nsvg.selectAll(\"path\")\n    .data(voronoi(samples).polygons())\n  .enter().append(\"path\")\n    .attr(\"d\", d =\u003e `M${d.join(\"L\")}Z`)\n    .style(\"fill\", d =\u003e color(d.point))\n    .style(\"stroke\", d =\u003e color(d.point));\n```\n\n### d4\n\n```javascript\nfunction Mesh() {\n  const paths = voronoi(samples)\n    .polygons()\n    .map(sample =\u003e (\n      \u003cpath\n        d={`M${sample.join('L')}Z`}\n        fill={color(sample.data)}\n        stroke={color(sample.data)}\n      /\u003e\n    ));\n\n  return (\n    \u003csvg width={width} height={height}\u003e\n      {paths}\n    \u003c/svg\u003e\n  );\n}\n```\n\nWe replace the mutating `select`, `selectAll`, `enter`, `append`, `data`, `attr`, and `style` with familiar React rendering of the points.\n\nAnimation is more complicated, but again, React can help. By using keys and the `ReactCSSTransitionGroup`, it's possible to describe animations in CSS, rather than using d3's interpolation. I haven't verified the performance, but I expect CSS transition group animations to be faster, since they're browser-native and avoid the JS engine. For example:\n\n```javascript\nd3.select(\"body\")\n    .style(\"color\", \"green\") // make the body green\n  .transition()\n    .style(\"color\", \"red\"); // then transition to red\n```\n\nBecomes (specifying the duration, which the original left out):\n\n```css\nbody {\n  transition: color 250ms;\n}\n```\n\n## Why we still need d3\n\nd3 does [a lot](https://github.com/d3/d3/blob/master/API.md) and we can continue to use most of it. In fact, these demos collectively use a [dozen d3 packages](https://github.com/joelburget/d4/blob/master/package.json). d3 is especially useful for calculating layouts and colors.\n\n## Future Work\n\nThere are some pieces of d3 that I would love to use but aren't easily portable. For example, [d3-drag](https://github.com/d3/d3-drag) and [d3-zoom](https://github.com/d3/d3-zoom) smooth over a lot of the quirks you'd have to deal with when implementing dragging and zooming, but they're only designed to work with d3 selections (eg `selection.call(d3.zoom().on(\"zoom\", zoomed));`).\n\nI'm curious about the performance of this approach. I haven't benchmarked yet, but my intuition is that it should be fast -- as fast as React's reconciliation. However, I don't know how that part of d3 is implemented, so maybe d3 is actually faster.\n\nA small thing -- it's possible to use only parts of d3. For example: `import {voronoi as d3Voronoi} from 'd3-voronoi';` instead of `d3.voronoi`, and `import {lab} from 'd3-color';` instead of `d3.color.lab`), but nobody uses it that way, so examples of the import style are hard to find (and it's often not obvious which name will be exported (`d3-geo` exports `geoArea` and `geoBounds` rather than `area` and `bounds`).\n\nBesides the five completed demos, I've also started working on a few others, but I'm deferring them to get this article published.\n\n## Demos\n\nIn all the demos we continue to use some d3 utilities, but use React to separate the logic from the display declaration. Take a look at the source for a few!\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoelburget%2Fd4","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjoelburget%2Fd4","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoelburget%2Fd4/lists"}