Embedded notebook still loading static imports dynamically?

I noticed that my new notebook-powered personal site loads a dependency, htl, from jsdelivr, despite the code being bundled using npm:

(you can see the full results on webpagespeedtest.org)

I thought that treating notebooks as npm modules (as described in advanced embedding) would let me self-host a notebook and its dependencies without depending on outside infrastructure.

It might be that I’m doing something incorrectly, or have a misapprehension about the nature of bundling code for deployment outside of observablehq.com. If there is a way to bundle together a notebook and its dependencies I’d be very curious to know how to do it.

Many thanks!

This dependency comes from the imported notebook:

Which requires htl from npm:

htl = require("htl@0.2")

This is not a static import — it’s a dynamic require, and as such, it isn’t treated specially when you download a notebook for embedded. The inclusion of transitive dependencies in the downloaded tarball only applies to notebook imports, not to require (and dynamic ES imports).

That said, you can remove this external dependency, too, by specifying a custom require resolver when you embed your notebook, and then pointing htl at a local copy rather than loading it from jsDelivr. That technique is described here:

And it would look something like this:

// Initialize the Observable Runtime, telling it to use our local copy of htl
// rather than loading one from a CDN.
const runtime = new Runtime(new Library(name => {
  switch (name) {
    case "htl@0.2": return htl;

Thank you for the great explanation. That makes a lot of sense – I hadn’t realized that the dynamic call was for a dynamic import.

Happy holidays! :christmas_tree: