npm compatibility
Tools that use npm
and node_modules
can use JSR through JSR’s npm
compatibility layer. Examples of such tools are, Node.js, Cloudflare
Workers, Vite, Esbuild, Webpack, and Rollup.
If you are using Deno, you can use
jsr:
imports.
With this compatibility layer, all JSR packages can be installed using existing
npm-compatible package managers like npm
, yarn
, and pnpm
. This allows you
to use JSR packages in any tool that supports npm packages using node_modules
.
When using this compatibility layer, you can install JSR packages using
npm install
/ yarn add
/ pnpm install
as usual. The JSR packages will be
downloaded from jsr.io and installed in your node_modules
directory.
Installing and using JSR packages
You can add JSR packages to your project using the jsr
CLI:
npx jsr add @luca/cases
This will add the @luca/cases
package to your package.json
file, and install
it to your node_modules
directory using your preferred package manager.
The package manager to use will be automatically detected based on the presence
of a package.lock
, yarn.lock
or pnpm-lock.yaml
file in your project. You
can also specify the package manager to use explicitly using the --npm
,
--yarn
, or --pnpm
flags to the jsr add
command.
You should check the
.npmrc
file that is created into source control. This enables future calls tonpm install
/yarn
/pnpm install
to succeed.
You can then use the JSR package from your code:
import { camelCase } from "@luca/cases";
Note: Due to limitations of
npm
andyarn
, they may sometimes install duplicate copies of your JSR dependencies. This can lead to largernode_modules
directories than necessary, and for some packages it can lead to unexpected behavior. We recommend usingpnpm
for the best experience.
Limitations
The JSR npm compatibility layer is not a perfect replacement for native JSR support. There are some limitations:
- You can not use
jsr:
specifiers to import JSR packages. - Unlike with native JSR imports, you are not directly importing TypeScript
code. Instead JSR transpiles the TypeScript code to JavaScript before it is
installed into your
node_modules
directory. This generally means that your editor experience will suffer, because “Go to definition” and other features will link to transpiled JavaScript code, or to generated.d.ts
files. - Install times will generally be slower than with native JSR support, because npm will download files even if they are not used in your project.
- You can not publish JSR packages using the npm compatibility layer, using
npm publish
. You can only publish JSR packages usingjsr publish
ordeno publish
.
Advanced setup
The JSR npm compatibility layer works by making all JSR packages available under
the special @jsr
npm scope. The @jsr
npm scope is not a real npm scope, and
you cannot publish packages to it. You can only use it to install jsr packages
from npm.
The @jsr
npm scope is served from the JSR registry at https://npm.jsr.io
.
This means that you need to configure your package manager to use this registry
to install JSR packages. When adding packages with the jsr
CLI, this is done
automatically.
Instead of using the jsr
CLI to install JSR packages, you can also manually
configure your package manager to support installing JSR packages.
To do this, create an .npmrc
file, and add the following lines to it:
@jsr:registry=https://npm.jsr.io
This instructs your package manager to load all packages in the @jsr
scope
from the JSR backend instead of npm.
You can also configure your package manager to support JSR in all projects on
your machine, by creating a .npmrc
file in your home directory with the same
content.
You can then manually install JSR packages using npm install
/ yarn add
/
pnpm install
as usual, using the special @jsr
npm scope:
npm install @jsr/luca__cases@1 # installs the @luca/cases package from JSR
yarn add @jsr/luca__cases@1 # installs the @luca/cases package from JSR
pnpm install @jsr/luca__cases@1 # installs the @luca/cases package from JSR
The name following the @jsr/
scope is the name of the JSR package you want to
install. This name is the same as the package name you’d use with jsr:
imports, except that the @
prefixing the scope is removed, and the /
between
the scope and the name is replaced with __
.
For example, the jsr:@luca/cases
package is available at @jsr/luca__cases
.
You can then import JSR packages using the @jsr
scope in your code:
import { camelCase } from "@jsr/luca__cases";
If you’d like to import packages without having to specify the @jsr/
, you can
update the dependencies
object in your package.json
:
// package.json
{
"type": "module",
"dependencies": {
- "@jsr/luca__cases": "1"
+ "@luca/cases": "npm:@jsr/luca__cases@1"
}
}
You can then import the package using the name defined in your package.json
:
import { camelCase } from "@luca/cases";
Technical details
The @jsr
npm scope is a special scope that is used to map JSR packages to npm
packages for systems that do not natively support JSR. The @jsr
npm scope is
not a real npm scope, and you cannot publish packages to it. You can only use it
to install JSR packages from npm.
@jsr
packages are not served from the npm registry at
https://registry.npmjs.org
. Instead, they are served from the JSR registry at
https://npm.jsr.io
. This endpoint implements the npm registry API. For example
to get the metadata for the @jsr/luca__cases
package, you can send a GET
request to https://npm.jsr.io/@jsr/luca__cases
.
This endpoint serves npm compatible tarballs for @jsr
packages. These tarballs
are generated by JSR, and contain all source code reachable from the entrypoint
of the package. This source code is transpiled to JavaScript, and TypeScript
type declarations (.d.ts
files) are generated for all TypeScript files. The
tarball also contains a package.json
file that contains the exports
field
from the original jsr.json
/ deno.json
file.
Yanked versions of packages are not advertised in the package version manifest of the npm registry endpoint. Tarballs for yanked versions are still available even when a version is yanked, which means that tools that have a reference to a yanked version in a lockfile will still be able to install that version.
The specific tarballs advertised for a given version of a package may change
over time, even if the version itself is not changed. This is because the JSR
registry may re-generate npm compatible tarballs for a package version to fix
compatibility issues with npm or improve the transpile output in the generated
tarball. We refer to this as the “revision” of a tarball. The revision of a
tarball is not advertised in the npm registry endpoint, but it is included in
the URL of the tarball itself and is included in the package.json
file in the
tarball at the _jsr_revision
field. The revision of a tarball is not
considered part of the package version, and does not affect semver resolution.
However, tarball URLs are immutable. Tools that have a reference to a specific tarball URL will always be able to download that exact tarball. When a new revision of a tarball is generated, the old tarball is not deleted and will continue to be available at the same URL. The new tarball will be available at a new URL that includes the new revision.
Because the tarball URL is included in package manager lock files, running
npm i
/ yarn
/ pnpm i
will never accidentally download a new revision of
the tarball.