esm.sh is a modern CDN that allows you to import es6 modules from a URL:
import Module from "https://esm.sh/PKG@SEMVER[/PATH]";
or build a module with custom input(code):
import { esm } from "https://esm.sh/build";
const { render } = await esm`
/* @jsx h */
import { h } from "npm:preact@10.13.2";
import { renderToString } from "npm:preact-render-to-string@6.0.2";
export const render () => renderToString(<h1>Hello world!</h1>);
`;
console.log(render()); // "<h1>Hello world!</h1>"
More usage check out here.
You may want to use bare specifier instead of URL with import maps:
<script type="importmap">
{
"imports": {
"react": "https://esm.sh/react@18.2.0"
}
}
</script>
<script type="module">
import React from "react" // alias to https://esm.sh/react@18.2.0
</script>
import React from "https://esm.sh/react@18.2.0";
You may also use a semver or a
dist-tag instead of a
fixed version number, or omit the version/tag entirely to use the latest
tag:
import React from "https://esm.sh/react"; // 18.2.0 (latest)
import React from "https://esm.sh/react@^17"; // 17.0.2
import React from "https://esm.sh/react@canary"; // 18.3.0-canary-7cd98ef2b-20230509
You can import submodules of a package:
import { renderToString } from "https://esm.sh/react-dom@18.2.0/server";
or import/fetch non-module(js) as following:
import "https://esm.sh/react@18.2.0/package.json" assert { type: "json" };
You can also import modules/assets from a github repo:
/gh/OWNER/REPO[@TAG]/PATH
. For example:
import tslib from "https://esm.sh/gh/microsoft/tslib@2.5.0";
or load a svg image from a github repo: https://esm.sh/gh/microsoft/fluentui-emoji/assets/Party%20popper/Color/party_popper_color.svg
By default, esm.sh rewrites import specifiers based on the package dependencies.
To specify the version of these dependencies, you can add the
?deps=PACKAGE@VERSION
query. To specify multiple dependencies, separate them
with a comma, like this: ?deps=react@17.0.2,react-dom@17.0.2
.
import React from "https://esm.sh/react@17.0.2";
import useSWR from "https://esm.sh/swr?deps=react@17.0.2";
import useSWR from "https://esm.sh/swr?alias=react:preact/compat";
in combination with ?deps
:
import useSWR from "https://esm.sh/swr?alias=react:preact/compat&deps=preact@10.5.14";
The original idea came from @lucacasonato.
By default, esm.sh exports a module with all its exported members. However, if
you want to import only a specific set of members, you can specify them by
adding a ?exports=foo,bar
query to the import statement.
import { __await, __rest } from "https://esm.sh/tslib"; // 7.3KB
import { __await, __rest } from "https://esm.sh/tslib?exports=__await,__rest"; // 489B
By using this feature, you can take advantage of tree shaking with esbuild and achieve a smaller bundle size. Note that this feature is only supported for ESM modules and not CJS modules.
By default, esm.sh bundles sub-modules in the dependency tree when you import a module.
You can also bundle all dependencies of a package into a single JS file by adding
?bundle-deps
query to the import URL:
import { Button } from "https://esm.sh/antd?bundle-deps";
Bundling deps can reduce the number of network requests and improve performance.
However, it may bundle shared code repeatedly. In extreme case, it may break the
side effects of the package, or change the import.meta.url
pointing. To avoid
this, you can add ?no-bundle
to disable the default bundling behavior:
import "https://esm.sh/@pyscript/core?no-bundle";
For package authors, you can specify the bundling strategy by adding the esm.sh
field to package.json
:
{
"name": "foo",
"esm.sh": {
"bundle": false, // disables bundling behavior
}
}
import React from "https://esm.sh/react?dev";
With the ?dev
option, esm.sh builds a module with process.env.NODE_ENV
set
to "development"
or based on the condition development
in the exports
field. This is useful for libraries that have different behavior in development
and production. For example, React will use a different warning message in
development mode.
By default, esm.sh checks the User-Agent
header to determine the build target.
You can also specify the target
by adding ?target
, available targets are:
es2015 - es2022, esnext, deno, and denonext.
import React from "https://esm.sh/react?target=es2020";
Other supported options of esbuild:
import foo from "https://esm.sh/foo?conditions=custom1,custom2";
import React from "https://esm.sh/react?keep-names";
import React from "https://esm.sh/react?ignore-annotations";
esm.sh supports ?worker
query to load the module as a web worker:
import workerFactory from "https://esm.sh/monaco-editor/esm/vs/editor/editor.worker?worker";
const worker = workerFactory();
You can pass some custom code snippet to the worker when calling the factory function:
const workerAddon = `
self.onmessage = function (e) {
console.log(e.data)
}
`;
const worker = workerFactory(workerAddon);
<link rel="stylesheet" href="https://esm.sh/monaco-editor?css">
This only works when the package imports CSS files in JS directly.
esm.sh supports importing wasm modules in JS directly, to do that, you need to
add ?module
query to the import URL:
import wasm from "https://esm.sh/@dqbd/tiktoken@1.0.3/tiktoken_bg.wasm?module";
const { exports } = new WebAssembly.Instance(wasm, imports);
Import Maps has been supported by
most modern browsers and Deno natively. This allows bare import
specifiers, such as import React from "react"
, to work.
esm.sh supports ?external=foo,bar
query to specify external dependencies. With
this query, esm.sh will not rewrite the import specifiers of the specified
dependencies. For example:
{
"imports": {
"preact": "https://esm.sh/preact@10.7.2",
"preact-render-to-string": "https://esm.sh/preact-render-to-string@5.2.0?external=preact"
}
}
Alternatively, you can mark all dependencies as external by adding a *
prefix before the package name:
{
"imports": {
"preact": "https://esm.sh/preact@10.7.2",
"preact-render-to-string": "https://esm.sh/*preact-render-to-string@5.2.0",
"swr": "https://esm.sh/*swr@1.3.0",
"react": "https://esm.sh/preact@10.7.2/compat"
}
}
Import maps supports
trailing slash
that can not work with URL search params friendly. To fix this issue, esm.sh
provides a special format for import URL that allows you to use query params
with trailing slash: change the query prefix ?
to &
and put it after the
package version.
{
"imports": {
"react-dom": "https://esm.sh/react-dom@18.2.0?pin=v135&dev",
"react-dom/": "https://esm.sh/react-dom@18.2.0&pin=v135&dev/"
}
}
esm.sh also provides a CLI Script in Deno to generate and update the import maps that resolves dependencies automatically.
In rare cases, you may want to request JS source files from packages, as-is,
without transformation into ES modules. To do so, you need to add a ?raw
query to the request URL.
For example, you might need to register a package's source script as a service worker
in a browser that does not yet support
the type: "module"
option:
await navigator.serviceWorker.register(
new URL(
"https://esm.sh/playground-elements/playground-service-worker.js?raw",
import.meta.url.href
),
{ scope: '/' }
);
You may alternatively use raw.esm.sh
as the origin, which is equivalent to
esm.sh/PATH?raw
:
<playground-project sandbox-base-url="https://raw.esm.sh/playground-elements/"
></playground-project>
so that transitive references in the raw assets will also be raw requests.
esm.sh is a Deno-friendly CDN that resolves Node's built-in modules (such as fs, os, etc.), making it compatible with Deno.
import express from "https://esm.sh/express";
const app = express();
app.get("/", (req, res) => {
res.send("Hello World");
});
app.listen(3000);
For users using deno < 1.31
, esm.sh uses
deno.land/std@0.177.0/node as node
compatibility layer. You can specify a different version by adding the
?deno-std=$VER
query:
import postcss from "https://esm.sh/postcss?deno-std=0.128.0";
Deno supports type definitions for modules with a types
field in their
package.json
file through the X-TypeScript-Types
header. This makes it
possible to have type checking and auto-completion when using those modules in
Deno.
(link).
In case the type definitions provided by the X-TypeScript-Types
header are
incorrect, you can disable it by adding the ?no-dts
query to the module import
URL:
import unescape from "https://esm.sh/lodash/unescape?no-dts";
This will prevent the X-TypeScript-Types
header from being included in the
network request, and you can manually specify the types for the imported module.
Nodejs(18+) supports http importing under the --experimental-network-imports
flag. Bun doesn't
support http modules yet.
We highly recommend Reejs as the runtime with esm.sh that works both in Nodejs and Bun.
This is an experimental API that allows you to build a module with custom input(code).
import build from "https://esm.sh/build";
const ret = await build({
dependencies: {
"preact": "^10.13.2",
"preact-render-to-string": "^6.0.2",
},
code: `
/* @jsx h */
import { h } from "preact";
import { renderToString } from "preact-render-to-string";
export function render(): string {
return renderToString(<h1>Hello world!</h1>);
}
`,
// for types checking and LSP completion
types: `
export function render(): string;
`,
});
// import module
const { render } = await import(ret.url);
// import bundled module
const { render } = await import(ret.bundleUrl);
render(); // "<h1>Hello world!</h1>"
or use the esm
tag function to build and import js/ts snippet quickly in browser
with npm packages:
import { esm } from "https://esm.sh/build";
const { render } = await esm`
/* @jsx h */
import { h } from "npm:preact@10.13.2";
import { renderToString } from "npm:preact-render-to-string@6.0.2";
export const render () => renderToString(<h1>Hello world!</h1>);
`;
console.log(render()); // "<h1>Hello world!</h1>"
To ensure stable and consistent behavior, you may want to pin the build version of a module you're using from esm.sh. This helps you avoid potential breaking changes in the module caused by updates to the esm.sh server.
The ?pin
query allows you to specify a specific build version of a module,
which is an immutable cached version stored on the esm.sh CDN.
import React from "https://esm.sh/react-dom?pin=v135";
// or use version prefix
import React from "https://esm.sh/v135/react-dom";
By using the ?pin
query in the import statement, you can rest assured that the
version of the module you're using will not change, even if updates are pushed
to the esm.sh server. This helps ensure the stability and reliability of your
application.
For UI libraries like React and Vue, esm.sh uses a special build version
stable
to ensure single version of the library is used in the whole
application.
esm.sh provides a CLI script for managing imports with import maps in Deno and Node/Bun (via Reejs). This CLI script automatically resolves dependencies and uses a pinned build version for stability.
To use the esm.sh CLI script, you first need to run the init
command in your
project's root directory:
deno run -A -r https://esm.sh init
Once you've initialized the script, you can use the following commands to manage your imports:
# Adding packages
deno task esm:add react react-dom # add multiple packages
deno task esm:add react@17.0.2 # specify version
deno task esm:add react:preact/compat # using alias
# Updating packages
deno task esm:update react react-dom # update specific packages
deno task esm:update # update all packages
# Removing packages
deno task esm:remove react react-dom
The CLI script works with Node/Bun via Reejs:
# Initializing
reejs x https://esm.sh init
# Using reejs tasks
reejs task esm:add react
reejs task esm:update react
reejs task esm:remove react
Which browsers are supported?
esm.sh supports all modern browsers which support ES6 Module. Including Deno.
Does esm.sh support private npm packages?
Nope, but you can deploy your own esm.sh server to support private npm packages. Check our self hosting guide.
Why I get timeout
error?
When first time you import a module, esm.sh needs to build the module then cache it. If the module is too large, it may take a long time to build. You can try again later, since it's queued and will be built soon.
Why I get The source code is invalid...
error in Deno?
Since Deno 1.18+ will generate the deno.lock
file for the dependencies, but esm.sh builds code with different version of esbuild when you imports modules from different build version. To fix this issue, please try to pin the build version like https://esm.sh/react?pin=v135
. Or use the CLI Script to manage the dependencies that will fix the build version automatically.
Does esm.sh support Subresource Integrity (SRI)?
Browsers don't yet support SRI for ES6 Module. Deno (1.18+) supports hash check by generating the deno.lock
file.
How can I report a bug?
Please open an issue on GitHub. Thanks :)