JSR: What Comes After NPM?

Matthew Tyson
10 Min Read

The JavaScript Registry (JSR) streamlines and enhances the security of building, sharing, and utilizing JavaScript packages, and functions independently or alongside NPM.

supply chain blockchain packages fullfillment center boxes automation by undefined getty
Credit: Getty Images

NPM, the Node Package Manager, handles millions of packages and billions of annual downloads. While it has served its purpose for years, it faces challenges with TypeScript build complexity and package provenance. Recent security breaches tied to NPM’s provenance issues have prompted many developers to seek alternatives.

The JavaScript Registry (JSR), conceived by Deno creator Ryan Dahl, aims to address these shortcomings. With enterprises already adopting it, it’s timely to examine the next evolution of JavaScript packaging.

The Intelligent JavaScript Registry

Regardless of whether you adopt it immediately, JSR warrants attention for two primary reasons. Firstly, it resolves significant technical deficiencies present in NPM, which are worth a closer look. Secondly, it’s rapidly gaining momentum and could soon achieve widespread adoption.

JSR employs an innovative strategy to tackle NPM’s known problems. For instance, it can deliver compiled (or type-stripped) JavaScript, even if the original package is TypeScript. Conversely, platforms natively supporting TypeScript, like Deno, will receive the original TypeScript. JSR also provides enhanced guarantees and robust security features absent in NPM, such as stricter package authentication and metadata tracking. Additionally, it automates package documentation generation.

While all package consumers will appreciate these capabilities, they are particularly beneficial for package creators. Authors no longer need to execute a TypeScript build step, simplifying and making the library publishing process more reproducible. This efficiency likely explains why prominent organizations like OpenAI and Supabase have already adopted JSR.

JSR dynamically fulfills package requests based on your application’s configuration, incurring minimal overhead. It also boasts extensive and seamless integration with NPM, making it relatively simple to try. JSR feels more like an advancement of NPM rather than a complete replacement.

How JSR Works

The management of TypeScript code within JavaScript environments has recently become a significant discussion point. While mixing TypeScript and JavaScript is common in development, it often leads to friction during the build process, necessitating prior compilation of TypeScript code. Although a permanent type stripping feature in TypeScript could eventually resolve this, JSR addresses it today by offloading compilation to the registry.

JSR is fundamentally designed as an advanced superset of the NPM registry, with the unique ability to differentiate between TypeScript and JavaScript.

When a package author publishes to JSR, they upload the TypeScript source directly, bypassing any build step. JSR then acts as an intelligent intermediary. If a package is requested from a Deno environment, JSR serves the original TypeScript version. However, if requested from a Node environment (via npm install), JSR automatically transpiles the code into ESM (ECMAScript Modules)-compliant JavaScript and generates the necessary type definition (.d.ts) files on the fly.

This capability effectively eliminates the “TypeScript tax” that has burdened library authors for years. There’s no longer a need to configure complex build pipelines or maintain separate distribution folders. Developers simply publish their code, and the registry handles its adaptation to the consumer’s runtime.

It’s also important to note that JSR has adopted the modern ESM format as its module system. This only becomes a consideration if you’re working with older packages based on CommonJS (CJS); using CJS with JSR is feasible but requires extra effort. JSR’s embrace of ESM is a forward-thinking move, aligning with the JavaScript ecosystem’s transition to a unified standard.

The ‘Slow Types’ Tradeoff

To facilitate its real-time transpilation, JSR enforces a constraint known as “no slow types.” JSR disallows global type inference for exported functions and classes, requiring you to explicitly type your exports (e.g., by defining a function’s return type rather than allowing the compiler to infer it). This aligns with common best practices.

Explicitly defined return types enable JSR to generate documentation and type definitions instantly, avoiding a complete, time-consuming compiler pass for every installation. This represents a minor adjustment for developers that results in a significant performance boost across the ecosystem.

Built-in Security Defaults

Perhaps most valuable for enterprise users, JSR directly addresses supply-chain security through robust provenance. It integrates with GitHub Actions via OpenID Connect.

When a package is published, JSR generates a transparency log (powered by Sigstore). This log cryptographically links the package within the registry to the specific commit and continuous integration (CI) run that produced it. Unlike the “blind trust” model of the traditional NPM registry, JSR allows you to verify that the code you are installing genuinely originates from its claimed source repository.

This also simplifies the process for package creators to implement their desired security measures without extensive manual configuration. While NPM has recently attempted to add trusted publishing, JSR has incorporated this functionality from its inception.

Getting Started with the JavaScript Registry

A key advantage of JSR is that it doesn’t necessitate abandoning your current development tools. You don’t need to install a new CLI or switch to Deno to use it. Thanks to a clever compatibility layer, it works seamlessly with npm, yarn, and pnpm right out of the box.

Integrating a JSR package into a standard Node project involves three simple steps.

1. Use JSR to Add the Package

Instead of the usual install command, you utilize the JSR tool to add packages. For example, to include a standard library date helper, you would type:

npx jsr add @std/datetime

Here, we employ the NPX command to execute the jsr tool once with the add command. This action integrates the datetime library into your current NPM project.

2. JSR Configures the Package Automatically

Behind the scenes, the npx jsr add command creates an .npmrc file in your project’s root. This file maps the @jsr scope to the JSR registry (an alias we’ll use shortly):

@jsr:registry=https://npm.jsr.io

You don’t need to manually manage this mapping; it simply instructs NPM, “For any @jsr package, connect with the JSR registry, not the public NPM registry.”

3. JSR Adds the Dependency

Next, JSR incorporates the package into your package.json using an alias. The dependency entry will appear similar to this:

"dependencies": {
  "@std/datetime": "npm:@jsr/std__datetime@^0.224.0"
}

Again, as a developer, you typically won’t need to inspect this. JSR allows you to treat the dependency as a standard one, while NPM and JSR manage its retrieval. In your program’s JavaScript files, the dependency is imported just like any other:

import { format } from "@std/datetime";

JSR and the Evolution of JavaScript

JSR originates from Ryan Dahl, the visionary behind Node.js. In his notable 2018 presentation, “10 things I regret about Node.js,” Dahl discussed how the ecosystem had veered from web standards, highlighted the complexity of node_modules, and pointed out the often opaque Node build process.

Dahl’s initial attempt to rectify these issues was Deno, a secure runtime that employs standard URLs for imports. However, JSR represents a more pragmatic shift. While changing runtimes is an option, the NPM ecosystem, with its 2.5 million packages, is too valuable to simply abandon.

JSR essentially provides a second opportunity to optimize the architecture. It applies Deno’s core principles—security by default and first-class TypeScript support—to the broader JavaScript landscape. Crucially, to ensure it serves the entire ecosystem (not just Deno users), the project has transitioned to an independent open governance board, solidifying its role as a neutral utility for all runtimes.

The Future of JSR and NPM

Is JSR positioned to be an “NPM killer”? Not yet; perhaps never. Instead, it functions as a catalyst for the evolving JavaScript ecosystem. It specifically addresses the critical, long-standing challenges of TypeScript publishing and supply-chain security that NPM was never designed to handle.

For architects and engineering leads, JSR presents a reliable choice for new internal tooling and libraries, particularly those developed in TypeScript. JSR’s seamless interoperability with NPM virtually eliminates the risk of vendor lock-in. Even if JSR were to disappear, you would still retain access to your source code.

Ultimately, JSR’s most compelling benefit is its simplicity: it allows developers to focus on writing code rather than managing complex build scripts. In a landscape where configuration often consumes more development time than actual logic, this feature alone makes JSR well worth exploring.

JavaScriptProgramming LanguagesSoftware DevelopmentTypeScriptApplication SecurityWeb Development
Share This Article
Leave a Comment

Leave a Reply

Your email address will not be published. Required fields are marked *