Skip to content

JSR - The cross-platform package manager for ESM

JSR - The cross-platform package manager for ESM

JSR - The cross-platform package manager for ESM

Move over NPM, there’s a new package manager in town. JSR is a new open-source package registry for JavaScript and TypeScript. This particular release has caught my attention, and I’m excited to explore if and where JSR fits into our overflowing ecosystem.

Setting the stage

The JSR website has a page called Why JSR? I want to explore the why a little bit further. I think that the why points to JSR potentially being the future of the JavaScript ecosystem.

NPM and JavaScript modules

If you’ve found yourself confused about publishing and consuming packages and modules in the modern ESM era, you’re not alone. When NodeJS was created, there was no standard for JS modules. Node introduced CommonJS modules, and NPM provided a way to publish and consume these modules in our applications. It’s been years now since ES Modules became a standard and we are still in a weird module purgatory with no clear end in sight. JSR represents a fresh start, free from the baggage of the Node and NPM ecosystems.

JavaScript runtimes and interoperability

New JavaScript runtimes are popping up every year. WinterCG (Web-interoperable Runtimes Community Group) was started to improve interoperability for the various runtimes with some standard APIs. Node has made steps towards this goal, but it might never get all the way there and NPM is coupled pretty tightly to that ecosystem. JSR supports several runtimes (depending on the package). This is a huge opportunity for it to become the package manager for the ecosystem at large. See cross-runtime support section for more details.

NPM Compatibility

JSR doesn’t end up being a complete departure from NPM since it includes an NPM compatibility layer. The docs highlight some limitations and provide deeper technical details on how this piece works. The simple overview is that you can install and use packages from JSR to a Node/NPM based project. The packages will get added to your package.json and node_modules directory. About the same as any package installed from NPM.

JSR Overview

At a high level, JSR is very similar to NPM. You can search for packages on the website. You can publish packages to their registry, and you can download packages from it as well. We touched on the two major features that set it apart from NPM: It only supports ESModules, and it has cross-runtime support. In the rest of the article we’ll explore the essentials (package management) and also some other really great features that feel like are bringing the JS package management ecosystem up to speed with our modern workflows.

Native TypeScript

I have to say I’m not surprised by this feature, considering JSR was developed by the same people behind Deno (a runtime that supports TS natively). It’s a great feature. If you haven’t figured it out yet TypeScript is a big deal and doesn’t seem to be fading away anytime soon. So what exactly does TS support look like in a package manager, though? If your runtime supports TypeScript natively (like Deno) - it can consume the TS files in your package directly. For environments that don’t, it will automatically compile your code to JavaScript and package it with type declaration files (.d.ts). Pretty, stinking, cool. The docs include a page called “about slow types”. It is probably worth a read on its own ,but the TLDR is that JSR will analyze your TS code and penalize you in certain ways if you have some type code that it considers to be slow. On the page, it outlines exactly what these penalties are, as well as what it considers slow types to be. Slow types will also come into play when we get into package scoring later in the article.

Packages

The docs have a page dedicated to packages. It covers some important stuff like deleting packages, versioning, documentation, and publishing. We’ll touch on some of it with an emphasis on the things that are done differently or better than with NPM.

jsr.json file

jsr.json is a configuration file that specifies a name, version, and exports of a package. It doesn’t include a list of dependencies like you would have in a package.json file. This is a configuration file for a package to be published to JSR. JSR dependencies are defined in an import map file or a package.json file, depending on the runtime.

Adding packages to a project

Adding packages to your project will vary depending on your target runtime. Here’s an example of installing it for Deno and NPM using various package managers. For Deno, an import map entry is created. For NPM, it gets installed as an NPM package using the JSR NPM compatibility layer.

# deno
deno add @luca/cases

# npm (one of the below, depending on your package manager)
npx jsr add @luca/cases
yarn dlx jsr add @luca/cases
pnpm dlx jsr add @luca/cases
bunx jsr add @luca/cases

If the runtime has native JSR support, you don’t need to explicitly install packages. You can important them using the jsr: scheme.

// Import a specific patch version
import { camelCase } from "jsr:@luca/cases@1.0.0";

// Import the latest version in a major version range
import { camelCase } from "jsr:@luca/cases@1";

// Import the latest version compatible with a specific version (>= 1.2.3 and < 2.0.0)
import { camelCase } from "jsr:@luca/cases@^1.2.3";

As you can see semantic versioning is supported and works similarly to NPM.

Publishing packages

If you want to publish a package to JSR, there are many important rules you need to be aware of. I recommend reading “Publishing packages” before moving forward with this. The main things to note are: ESModules only, npm packages are supported, JSR packages are supported, and node APIs are supported. So there aren’t many constraints. You just need to understand how exactly to do these things. IE: how do I use NPM dependencies, how do the imports work, etc? Once you’ve got all that down, you will be ready to publish your first package to JSR 😃

Documentation

JSR emphasizes quality package documentation. It’s one of the factors in their package scoring algorithm, which we will discuss shortly. The important parts of a package’s documentation include:

  • a README.md file
  • symbol documentation - functions, interfaces, classes, etc
  • module documentation - docs for each exported module in a package The symbol and module documentation are written using JSDoc. The JSR website generates some nice documentation based on these 3 pieces on your package page. This means you can understand a package's entire public API without ever leaving jsr.io.

Browser support

The cross-runtime support is amazing, but I was curious where the original JavaScript runtime fit (the browser). According to the documentation: “You can use JSR with any tool that supports ES modules, and either has native support for JSR or supports npm packages using node_modules.” Since modern web browsers support ES modules, I will count this as JSR support. From what I can tell, though, there is one caveat. To include a module on a web page, we need a URL to download it from. Can we publish our package to JSR and use it to load a module via a script tag? According to the JSR usage policy, no. The “Unacceptable use” section states: “It is not acceptable to use JSR as a CDN for serving assets directly to web applications in a browser, except for development purposes.” So, it seems that this is a no-go for now. We will probably need to wait a bit for CDN services like jsdelivr to pull packages from the JSR registry, similar to what they do with NPM currently.

Other notable features

We’ve covered the most important bits about package management, TS support, NPM compatibility, etc. But there are still a few unique features that add an extra bit of sparkle to JSR.

Scoring

We mentioned earlier that documentation plays a part in a scoring algorithm. JSR analyzes all of the packages published to its registry and assigns them a score based on certain criteria: documentation, best practices, discoverability, and compatibility. This score is shown with every package listed on the website. You can also add a shiny badge to your README if you’re particularly proud of something. It’s still early on, so it’s likely this will evolve as time goes on. It’s an interesting gimmick, at the very least!

Summary

I am excited to have a new cross-platform compatible package manager that only supports ESM. As useful as NPM has been all these years, it carries a lot of baggage from “the olden days”. JSR’s native TypeScript support and reliance on modern standards is a breath of fresh air. It feels like I can publish a simple package without much ceremony. I’m excited to release my first package to JSR soon :)

This Dot is a consultancy dedicated to guiding companies through their modernization and digital transformation journeys. Specializing in replatforming, modernizing, and launching new initiatives, we stand out by taking true ownership of your engineering projects.

We love helping teams with projects that have missed their deadlines or helping keep your strategic digital initiatives on course. Check out our case studies and our clients that trust us with their engineering.