How can I load d3.js with Webpacker in Rails 6?

Viewed 1062

I'm trying to use d3.js in a Rails 6 app with Webpacker and I'm getting Uncaught ReferenceError: d3 is not defined.

I added d3 using yarn add d3.

My package.json file:

{
  "name": "example",
  "private": true,
  "dependencies": {
    ...
    "d3": "^5.15.0",
    ...
  },
  "version": "0.1.0",
  "devDependencies": {
    "webpack-dev-server": "^3.1.14"
  }
}

/app/javascript/packs/application.js:

require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
require("d3")
...

In another JS file I added the following as a way to ensure it's loading properly:

...
console.log("test");
const div = d3.selectAll("div");
...

I ran rails s to restart the Rails server, then reloaded the page.

The log statement is appearing in the console but the d3 line is returning Uncaught ReferenceError: d3 is not defined.

2 Answers

This is what has worked best for me (adding 2 options, easy and efficient):

1. Add the package via yarn

yarn add d3

Easy

Load the entire d3 library.

2. config/webpack/environment.js

Create an alias for the package:

const aliasConfig = {
  'd3': 'd3/dist/d3.js'
};

environment.config.set('resolve.alias', aliasConfig);

The alias has to point to the main JS file of the package, relative to the node_modules folder.

3. app/javascript/packs/application.js

Add the import:

import "d3"

Efficient

Load only the functions you are going to use.

2. config/webpack/environment.js

No need to add alias using this workflow.

3. app/javascript/packs/application.js

import specific functions from d3 submodules, and assign them to a global window.d3 object:

import { select, selectAll } from 'd3-selection'
import { scaleLinear } from 'd3-scale'
import { min, max } from 'd3-array'
...

window.d3 = Object.assign(
  {},
  {
    select,
    selectAll,
    scaleLinear,
    min,
    max
    ...
  }
)
Related