JavaScript: Imports
You can load a library using an import
statement. For example:
import confetti from "npm:canvas-confetti";
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";
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.
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">
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");
import
instead of require
: it’s the modern standard, more reliable, more forward-looking, and faster.