A fast, smart, & global content delivery network (CDN) for modern(es2015+) web development.
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 use bare specifier instead of URL with import maps:
<script type="importmap">
{
"imports": {
"react": "https://esm.sh/[email protected]"
}
}
</script>
<script type="module">
import React from "react" // alias to https://esm.sh/[email protected]
</script>
More details check out here.
import React from "https://esm.sh/[email protected]";
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-e1ad4aa36-20230601
You can import submodules of a package:
import { renderToString } from "https://esm.sh/[email protected]/server";
or fetch non-module as following:
const pkg = await fetch("https://esm.sh/[email protected]/package.json").then(
(res) => res.json(),
);
esm.sh supports to import modules/assets from a github repo: /gh/OWNER/REPO[@TAG]/PATH
. For example:
import tslib from "https://esm.sh/gh/microsoft/[email protected]";
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: [email protected],[email protected]
.
import React from "https://esm.sh/[email protected]";
import useSWR from "https://esm.sh/[email protected]";
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&[email protected]";
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 that ain't declared in the exports
field.
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 ?bundle=false
to disable the default bundling behavior:
import "https://esm.sh/@pyscript/core?bundle=false";
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
}
}
esm.sh supports ?standalone
query to bundle all dependencies(except deps in peerDependencies
) into a single JS file.
import { Button } from "https://esm.sh/antd?standalone";
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, denonext, node and
bun.
import React from "https://esm.sh/react?target=es2020";
Other supported options of esbuild:
- Conditions
import foo from "https://esm.sh/foo?conditions=custom1,custom2";
- Keep names
import foo from "https://esm.sh/foo?keep-names";
- Ignore annotations
import foo from "https://esm.sh/foo?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/[email protected]/tiktoken_bg.wasm?module";
const { exports } = new WebAssembly.Instance(wasm, imports);
Note: The
?module
query requires the top-level-await feature to be supported by the runtime/browser.
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/[email protected]",
"preact-render-to-string": "https://esm.sh/[email protected]?external=preact"
}
}
Alternatively, you can mark all dependencies as external by adding a *
prefix before the package name:
{
"imports": {
"preact": "https://esm.sh/[email protected]",
"preact-render-to-string": "https://esm.sh/*[email protected]",
"swr": "https://esm.sh/*[email protected]",
"react": "https://esm.sh/[email protected]/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/[email protected]?pin=v135&dev",
"react-dom/": "https://esm.sh/[email protected]&pin=v135&dev/"
}
}
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, net, 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.33.2
, esm.sh uses deno.land/[email protected]/node as the
node compatibility layer. You can specify a different version by adding the ?deno-std=$VER
query:
import postcss from "https://esm.sh/express?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).
- Imports NPM/GH packages
- Supports TS/JSX syntaxes
- Bundle mulitple modules into a single JS file
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 const render () => 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:[email protected]";
import { renderToString } from "npm:[email protected]";
export const render () => renderToString(<h1>Hello world!</h1>);
`;
console.log(render()); // "<h1>Hello world!</h1>"
The Global CDN of esm.sh is provided by Cloudflare, one of the world's largest and fastest cloud network platforms.
To host esm.sh by yourself, check the hosting documentation.