DEV Community

Adrien Poly
Adrien Poly

Posted on

Dynamic module import with Stimulus JS

Scoped behavior by default

Being built on top of the mutation observer, Stimulus Controllers are only loaded once a data-controller="my-controller" is visible within the DOM.

This is a great way to scope JS behavior to an element within a page.

With the typical Webpack loader, all the Controllers are bundled within the pack.

// src/application.js
import { Application } from "stimulus"
import { definitionsFromContext } from "stimulus/webpack-helpers"

const application = Application.start()
const context = require.context("./controllers", true, /\.js$/)
application.load(definitionsFromContext(context))
Enter fullscreen mode Exit fullscreen mode

Importing large ES modules

When importing very large JS library you might want to only load them only on pages where the controller is used to avoid loading too much JS on other pages.

One nice feature of Webpack is the ability to dynamically load modules.

Using the lifecycle of Stimulus Controllers you can stick you dynamic import right into the initialize() or connect() function.

Here is and example to dynamically load moment

import { Controller } from "stimulus";

export default class extends Controller {

  connect() {
    import("moment").then(moment => {
      this.moment = moment.default;
    });
  }

  doSomething() {
    const formatedTime = this.moment().format("dddd, MMMM Do YYYY, h:mm:ss a");
  }
}
Enter fullscreen mode Exit fullscreen mode

and a working demo

Top comments (7)

Collapse
 
leastbad profile image
leastbad

Woah, what!?

How does this actually work? Is it generating a second pack and downloading it somehow? Are we talking about a second HTTP request?

Is the dynamically imported module cached? That is, if my controller mounts twice (or I have many objects with a given controller) does it move any additional data over the wire?

Big picture... doesn't this make the larger discussion around complicated code-splitting techniques discussed by React developers entirely moot?

As usual, thanks for the brilliant tip.

Collapse
 
adrienpoly profile image
Adrien Poly

Yes it will make a second http call. Lots of magic to me on how this actually works...

Cache works in the same way as your main cache

Collapse
 
leastbad profile image
leastbad

I'm trying to get my head around why this isn't a bigger deal. Again, it would seem to make the masochistic steps React devs take to split code seem ridiculous and moot.

Surely it can't actually be this simple?

Collapse
 
intermundos profile image
intermundos

Actually you can do it anywhere with webpack, not only Stimulus.

Thanks for quick tip!

Collapse
 
adrienpoly profile image
Adrien Poly

Yes I didn't emphasize on it. You are right, this is a Webpack feature (probably also supported by other bundlers as it is a JS feature developer.mozilla.org/en-US/docs/W...).

The nice thing with Stimulus is that it pairs very well with the lifecycle events.

Collapse
 
intermundos profile image
intermundos

Indeed, forgot that browser modules have native support for dynamic imports.

Collapse
 
konnorrogers profile image
Konnor Rogers

Just found this, so I'm a little late to the party, but rollup also has first class support for dynamic imports.

rollupjs.org/guide/en/#code-splitting