Observable Framework 1.0.0 GitHub

JavaScript: Imports

You can load a library using an import statement. For example:

import confetti from "npm:canvas-confetti";
Thanks to reactivity, imports can live in a JavaScript block anywhere on the page. But by convention, imports are commonly placed at the top of source files for readability.

You can use an imported library anywhere on the page.

Inputs.button("Throw confetti!", {reduce: () => confetti()})

npm imports

An npm: specifier, as shown above, denotes that the imported library will be loaded from npm rather than from node_modules. You don’t have to install imported libraries beforehand — just import, and the cloud shall provide. (This convention is also used by Deno.)

Under the hood, npm: imports are powered by jsDelivr’s esm.run CDN. The import above is thus equivalent to:

import confetti from "https://cdn.jsdelivr.net/npm/canvas-confetti/+esm";
We plan on supporting importing from node_modules using bare module specifiers (e.g., import confetti from "canvas-confetti"). If you’d like this feature, please upvote #360.

To load a specific version of a library, add a semver range:

import confetti from "npm:canvas-confetti@1";

To load a specific entry point, add a slash and the desired path:

import confetti from "npm:canvas-confetti@1/dist/confetti.module.mjs";

If you’re having difficulty getting an import working, it may help to browse the package and see what files are available as well as what’s exported in the package.json. You can browse the contents of a published module via jsDelivr; for example, for canvas-confetti see https://cdn.jsdelivr.net/npm/canvas-confetti/.

Local imports

In addition to npm, you can import JavaScript from local modules. This is useful for organizing your code: you can move JavaScript out of Markdown and create components and helpers that can be imported across multiple pages. This also means you can write unit tests for your code, and share code with any other web applications.

For example, if this is foo.js:

export const foo = 42;

Then you can say

import {foo} from "./foo.js";

and the imported value of foo is: .

Observable Framework automatically watches imported local modules during preview, so any changes to these files will instantly update in the browser via hot module replacement.

While there is reactivity across JavaScript code blocks in Markdown, there’s no reactivity within a JavaScript module. However, you can write async functions and generator functions to define reactive variables. And you can import the Observable standard library into local modules, so you can reference files and use other standard library features.

Module preloads

During build, Observable Framework will resolve the current exact version of the imported library from npm. Importing npm:canvas-confetti is thus equivalent to:

import confetti from "https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.2/+esm";

Version resolution locks the version of imported libraries so you don’t have to worry about new releases breaking your built site in the future. At the same time, you’ll conveniently get the latest version of libraries during local development and the next time you build.

In addition to resolving versions of directly-imported modules, Observable Framework recursively resolves dependencies, too! All transitively imported modules are automatically preloaded, greatly improving page load speed because the browser requests all imported modules in parallel.

<link rel="modulepreload" href="https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.2/+esm">
We’d also like to download imported modules from the CDN during build, making the built site entirely self-contained; see #20. This would further enable subresource integrity hashes; see #306.

Implicit imports

For convenience, Observable Framework provides recommended libraries by default in Markdown. These implicit imports are only evaluated if you reference the corresponding symbol and hence don’t add overhead if you don’t use them; for example, D3 won’t be loaded unless you have an unbound reference to d3.

Click on any of the imported symbols below to learn more.

import {FileAttachment} from "npm:@observablehq/stdlib";
import {Generators} from "npm:@observablehq/stdlib";
import {Mutable} from "npm:@observablehq/stdlib";
import dot from "npm:@observablehq/dot";
import * as duckdb from "npm:@duckdb/duckdb-wasm";
import {DuckDBClient} from "npm:@observablehq/duckdb";
import * as Inputs from "npm:@observablehq/inputs";
import mapboxgl from "npm:mapbox-gl";
import mermaid from "npm:@observablehq/mermaid";
import * as Plot from "npm:@observablehq/plot";
import SQLite from "npm:@observablehq/sqlite";
import {SQLiteDatabaseClient} from "npm:@observablehq/sqlite";
import tex from "npm:@observablehq/tex";
import * as Arrow from "npm:apache-arrow";
import * as aq from "npm:arquero";
import * as d3 from "npm:d3";
import * as htl from "npm:htl";
import {html} from "npm:htl";
import {svg} from "npm:htl";
import * as L from "npm:leaflet";
import _ from "npm:lodash";
import * as topojson from "npm:topojson-client";

Require

If you’re familiar with Observable notebooks, you may have noticed that we don’t mention require above. We recommend that you avoid require as the underlying Asynchronous Module Definition (AMD) convention has been made obsolete by standard imports in JavaScript, and AMD tends to be implemented inconsistently by libraries.

If you really need require, you can import it from d3-require:

import {require} from "npm:d3-require";

Then you can call require like so:

const d3 = require("d3@5");
We recommend that you use import instead of require: it’s the modern standard, more reliable, more forward-looking, and faster.