npm vs JSR Package Registry Comparison 2026
TL;DR
JSR (JavaScript Registry) is TypeScript-first, ESM-only, and ships Sigstore provenance on every package by default. npm has 2.5M packages, universal tooling support, and a decade of ecosystem inertia. In 2026, JSR is the better choice for new TypeScript packages targeting modern runtimes; npm remains the default for anything that needs maximum compatibility. The two registries are complementary: JSR packages can be installed with npm via a compatibility shim, so publishing to JSR doesn't mean abandoning npm users.
Key Takeaways
- JSR generates JavaScript and type declarations from TypeScript source — no build step required for publishers
- JSR is ESM-only; npm supports both ESM and CJS
- Every JSR package gets Sigstore provenance attestation automatically; npm provenance requires
--provenanceflag and CI setup - JSR packages are installable via npm using
@jsr/scope__packagenaming:npm install @jsr/scope__package - JSR's package scoring system rates documentation, TypeScript types, and description quality — visible to consumers
- JSR is designed by the Deno team but works in Node.js, Bun, Cloudflare Workers, and browsers
What Is JSR?
JSR (JavaScript Registry) launched in public beta in early 2024 as a modern alternative to npm, developed by the Deno team. It's designed to address the pain points that have accumulated in npm's architecture over two decades: the build step requirement for TypeScript packages, the CJS/ESM dual-publishing complexity, the inconsistent provenance story, and the lack of quality signals for consumers.
JSR isn't a replacement for npm — it's a complementary registry focused on a specific use case: TypeScript-native packages for modern JavaScript runtimes.
The key architectural differences between JSR and npm:
| Dimension | npm | JSR |
|---|---|---|
| Languages | JavaScript (TypeScript transpiled) | TypeScript source (native) |
| Module format | CJS, ESM, or both | ESM only |
| Provenance | Optional (--provenance flag) | Built-in on every package |
| Build step | Required for TypeScript | None — JSR handles it |
| Runtime support | Node.js primary | Deno, Node.js, Bun, browsers |
| Package count | 2.5M+ | ~15K (growing) |
| Scopes | Optional | Required (@scope/package) |
JSR's TypeScript-Native Publishing Model
The biggest developer experience improvement JSR offers is eliminating the build step for TypeScript packages. On npm, a TypeScript library author must:
- Write TypeScript source
- Configure
tsconfig.jsonfor output - Set up a bundler (tsup, Rollup, unbuild) for CJS/ESM dual output
- Generate
.d.tstype declarations - Configure
package.jsonexports field - Publish the compiled output
On JSR, the workflow is:
- Write TypeScript source
- Create
jsr.jsonwith name and version npx jsr publish
JSR's registry compiles the TypeScript, generates the type declarations, and makes the JavaScript available for consumers. The source TypeScript is also stored and visible to consumers who want to read the original.
// jsr.json
{
"name": "@myorg/my-package",
"version": "0.1.0",
"exports": "./mod.ts"
}
# Publish to JSR
npx jsr publish
# Or with the Deno CLI
deno publish
There's no dist/ directory, no bundler configuration, no dual-output setup. The source is the package.
ESM-Only: Constraint or Feature?
JSR publishes ESM only. This is a deliberate architectural decision rather than a limitation.
From JSR's perspective, ESM is the future of JavaScript modules and has been the standard for years. CJS compatibility in the npm ecosystem creates ongoing complexity for library authors — the dual CJS/ESM publish problem, the "exports" field complexity, the .mjs/.cjs extension dance. JSR takes a principled stance: new packages don't need to carry CJS support.
From a consumer perspective, this matters differently across environments:
Modern environments (ESM is fine):
- Deno (native ESM)
- Bun (native ESM)
- Cloudflare Workers (ESM)
- Node.js 22+ (native ESM import, and now
require()of ESM is supported) - Browser (native ESM)
Environments that need CJS:
- Older Node.js codebases using
require()without dynamic import - Some testing frameworks not yet fully ESM-compatible
- Legacy bundler configurations
For new packages in 2026, the ESM-only constraint is increasingly minor. Node.js 22 added support for synchronous require() of ESM modules, which was the last major blocker for ESM adoption in Node.js codebases. For the full migration context, see ESM Migration: CommonJS to ESM Guide 2026.
Provenance by Default
Every package published to JSR gets a Sigstore provenance attestation automatically. No configuration required, no special flags, no CI environment dependency.
JSR's provenance attestation:
- Links the published package to the source repository
- Links to the specific commit that triggered the publication
- Creates a transparency log entry that anyone can verify
- Shows a provenance badge on the package page visible to consumers
On npm, provenance requires:
- Running in a GitHub Actions (or similar OIDC-enabled) CI environment
- Setting
permissions: id-token: writein the workflow - Running
npm publish --provenance
For actively maintained packages, the npm provenance setup is straightforward. But JSR's approach — provenance as the default rather than the opt-in — means the baseline security level for JSR packages is higher than for npm packages where most publishers haven't configured provenance.
This matters for package consumers making trust decisions. A JSR package's provenance attestation is guaranteed; an npm package's is present only if the publisher configured it.
Installing JSR Packages Without Deno
A key source of confusion about JSR is the assumption that you need Deno to use it. This is incorrect. JSR packages can be installed with any package manager through JSR's npm compatibility layer.
# Install a JSR package with npm
npm install @jsr/scope__package
# The naming convention: @jsr/ prefix, then scope__package
# @scope/package → @jsr/scope__package (double underscore)
# With pnpm
pnpm add @jsr/scope__package
# With yarn
yarn add @jsr/scope__package
# With bun
bun add @jsr/scope__package
JSR maintains an npm-compatible registry at npm.jsr.io. When you install @jsr/scope__package, JSR automatically provides the transpiled JavaScript and type declarations in a format npm can consume.
This means:
- JSR is a publishing choice, not an infrastructure commitment
- Publishing to JSR doesn't exclude npm users
- Consumers don't need to know or care that a package came from JSR
The naming convention (@jsr/scope__package with double underscore) is the only visible seam between JSR and npm for consumers who install via npm.
JSR Package Scoring
JSR's package pages display a quality score (0-100) that rates packages across several dimensions:
Documentation score: Does the package have a populated README? Are exported symbols documented with JSDoc comments? JSR renders TypeScript source documentation inline on the package page, so JSDoc comments on exported functions, classes, and interfaces appear directly in the consumer's browser.
TypeScript score: Is the package written in TypeScript? Are all exports properly typed? JSR rewards packages that provide the best TypeScript experience rather than treating types as an afterthought.
Compatibility score: Does the package work across multiple runtimes? JSR scores packages higher if they're tested on Node.js, Deno, Bun, and browser environments rather than being Node.js-specific.
Description and version score: Does the package have a description? Has it progressed beyond 0.1.0? These signals help consumers distinguish maintained packages from abandoned experiments.
The scoring system creates an incentive structure for quality that npm's pure download count approach lacks. A well-documented, well-typed package with 1,000 downloads scores higher than a poorly documented package with 100,000 downloads.
When to Choose JSR Over npm
Publish to JSR when:
You're building a TypeScript-first library. JSR's build-free publishing, native TypeScript source display, and automatic type documentation generation create a significantly better experience for TypeScript consumers.
Your package targets modern runtimes. If your intended audience is on Deno, Bun, Cloudflare Workers, or modern Node.js, the ESM-only constraint isn't meaningful.
Provenance and supply chain security matter. For security-sensitive packages (auth libraries, cryptographic utilities, data parsers), JSR's automatic provenance provides trust signals without additional CI configuration.
You want better TypeScript documentation tools. JSR's package pages display TypeScript source with rendered JSDoc comments, interactive type exploration, and cross-reference navigation. The documentation experience for consumers is substantially richer than npmjs.com's README-only display.
Publish to npm (not just JSR) when:
You need CJS support. If your consumers are on older Node.js codebases or build systems that require CommonJS, npm is the right registry.
Maximum ecosystem reach matters. npm's 2.5M+ packages, decades of tooling integration, and universal recognition make it the default choice when you need broad compatibility.
Your package is an application, not a library. CLIs, build tools, and application scaffolders are typically npm packages — users install them with npx or globally, and the runtime is known.
The best of both worlds: Publish to both JSR and npm. Maintain a single TypeScript source in JSR, and use JSR's npm compatibility layer for npm users. Some package authors use JSR as the canonical registry and let JSR's npm shim handle npm consumers automatically.
JSR vs npm Package Discovery
For package consumers, the discovery experience differs meaningfully between the two registries.
npmjs.com:
- Search across 2.5M packages
- Download statistics are prominent
- Dependent package counts visible
- README rendered on package page
- No built-in quality scoring
jsr.io:
- Smaller but curated ecosystem (~15K packages)
- Quality score prominently displayed
- TypeScript source browsable inline
- JSDoc documentation rendered natively
- Dependency graph interactive visualization
- Provenance attestation visible and linked
For finding packages with specific behavior, npmjs.com's larger index is valuable. For evaluating TypeScript package quality, jsr.io's inline documentation and scoring provide better signal.
Publishing to Both Registries
For teams wanting the broadest reach while taking advantage of JSR's improvements, publishing to both registries is straightforward.
The primary publication stays in JSR. The npm presence is handled through either:
- JSR's automatic npm compatibility layer (consumers install via
@jsr/scope__package) - A separate npm publish for teams that want the same package available under its natural
@scope/packagename on npm
For option 2 with the same codebase:
// jsr.json
{
"name": "@myorg/my-package",
"version": "0.2.0",
"exports": "./mod.ts"
}
// package.json (for npm dual-publish)
{
"name": "@myorg/my-package",
"version": "0.2.0",
"main": "./dist/index.cjs",
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs",
"types": "./dist/index.d.ts"
}
}
}
The npm publish still requires a build step for dual CJS/ESM output. For the complete npm publishing workflow including granular access tokens and provenance configuration, see Publishing an npm Package: Complete Guide 2026.
The Ecosystem Trajectory
JSR's package count is growing but remains a fraction of npm. The momentum indicators for JSR are positive: Deno's mainstream adoption, Cloudflare Workers' growth as a JavaScript runtime, and Node.js 22's improved ESM support all reduce the friction of ESM-only packages.
The Deno team's continued investment in JSR, combined with the tooling improvements (native TypeScript compilation, automatic documentation, built-in provenance) makes JSR an increasingly compelling choice for new TypeScript packages in 2026.
For existing packages, the migration calculus is less clear. A package with millions of npm downloads has little incentive to move. For new packages targeting modern TypeScript consumers, JSR's developer experience improvements and supply chain security defaults make it worth considering as the primary registry.
The supply chain security angle is increasingly important. For a broader view of npm supply chain risks and defenses that motivate both JSR's provenance-by-default and npm's provenance flag, see npm Supply Chain Security Guide 2026.
Why JSR Exists: The Problems with npm
To understand what JSR is trying to accomplish, you need to understand the accumulation of technical debt that npm has acquired since it launched in 2010. npm was designed for a specific context: distributing Node.js packages in a world where JavaScript ran only on the server, the module system was CommonJS, TypeScript did not exist, and "edge runtime" was not yet a concept. The registry worked brilliantly for that context. The problem is that npm's architecture was essentially frozen in place by its own success — changing anything fundamental about how npm works risks breaking millions of packages that depend on its current behavior, so the inertia is enormous.
The CommonJS/ESM dual-publish complexity is the most painful manifestation of this. When the JavaScript ecosystem moved toward ECMAScript modules as the standard, npm couldn't simply switch over — the two module systems are not fully interoperable in Node.js, and changing the default would have broken the existing ecosystem. The result is that package authors who want to support both older CommonJS consumers and modern ESM environments have to publish two copies of their code: a CommonJS build and an ESM build, wired together with a carefully structured exports field in package.json. The build configuration for this is non-trivial. The exports field syntax is unintuitive. Tooling like tsup, unbuild, and Rollup exists largely to make this dual-publish process manageable. It works, but it is genuinely complex in a way that shouldn't be necessary.
TypeScript adds another layer of friction. npm has no native understanding of TypeScript — it is a JavaScript registry. When you publish a TypeScript package to npm, you must first compile it to JavaScript, then generate separate .d.ts type declaration files, and ensure those declaration files accurately describe the compiled output. The source TypeScript and the published JavaScript are different artifacts, which creates a mismatch: what consumers read in the type declarations may not perfectly reflect what they're actually executing. The tooling for generating declaration files has improved, but the conceptual gap between "what the author wrote" and "what gets published" remains.
The Node.js-only assumption baked into npm's tooling and package conventions is increasingly a liability. Most npm packages implicitly assume a Node.js runtime — they use process, Buffer, __dirname, and other Node.js globals that don't exist in Deno, Cloudflare Workers, or browser environments. Packages that want to be truly cross-runtime have to carefully avoid these assumptions, but there's no registry-level signal or enforcement to help consumers identify runtime compatibility.
JSR's architecture is a deliberate response to each of these problems. The registry accepts TypeScript source files directly and handles the compilation itself — there is no build step for publishers, no tsconfig.json to configure for output, no bundler to set up. Because JSR is ESM-only, there is no dual-publish complexity; every package ships the same module format. The registry generates type declarations from the TypeScript source automatically, keeping the source and the published types perfectly in sync. The scoring system rewards cross-runtime compatibility, creating an incentive for authors to write packages that work in Node.js, Deno, Bun, and browsers rather than being implicitly Node.js-only.
Ecosystem Maturity and Adoption Reality in 2026
JSR's technical design is compelling. Its adoption trajectory in 2026 is more nuanced, and an honest assessment requires separating the question of "is JSR well-designed?" from "will JSR succeed?" Those are different questions with different answers.
The packages that have moved to JSR are predominantly from the Deno ecosystem's inner circle: Deno's own standard library is published on JSR, and several prominent Deno-first tools like Hono (the lightweight web framework) have JSR presence. The publishing experience for these packages is excellent — the build-free TypeScript publishing, automatic documentation generation from JSDoc comments, and built-in provenance make JSR noticeably better than npm for this class of TypeScript-first, modern-runtime packages.
The vast majority of npm's catalog has not moved to JSR, and the economic incentive to do so is limited for established packages. A package with five million weekly npm downloads has no particular reason to add JSR publishing — the existing users are on npm, the tooling their projects use points at npm, and the incremental audience gain from JSR is small. For those packages, JSR publishing would mean maintaining two publication workflows, two sets of release automation, and two places where users might report issues. The effort-to-benefit ratio is unfavorable unless the package author has strong personal interest in JSR's mission.
Deno 2.0's npm compatibility story has also reduced JSR's urgency as the default registry for Deno-based projects. Deno can now import npm packages directly with the npm: specifier — import express from "npm:express" works in Deno without any additional configuration. This means Deno developers have access to npm's entire two-and-a-half-million-package catalog without needing those packages to be on JSR first. The driving rationale for "publish to JSR or Deno users can't use it" no longer holds, which reduces one of the key motivations for npm package authors to add JSR support.
Node.js 22's addition of synchronous require() support for ESM modules addressed the last major blocker for ESM-only packages in Node.js environments. This is good for JSR because it means JSR's ESM-only constraint is less of an obstacle for Node.js consumers. But it's also meaningful for npm, because npm packages can now publish ESM-only without the CJS compatibility shim, reducing one of JSR's differentiation points.
The honest assessment for 2026 is that JSR is a genuine improvement in registry design that is worth using for new TypeScript-first packages, particularly those targeting multiple runtimes. Publishing to JSR costs little if you're already writing TypeScript — the workflow is simpler than npm, not more complex. The quality score system, automatic documentation, and built-in provenance create a better default publishing experience. For libraries that primarily target Deno or Cloudflare Workers, JSR should be the primary registry with npm as a secondary consideration.
For the broader npm ecosystem, JSR will remain a specialized choice for the foreseeable future. The switching costs for established packages are high, the compatibility story between JSR and npm is good enough that urgency is low, and the developer audience is accustomed to npm in a way that creates real inertia. Expecting JSR to meaningfully challenge npm's catalog dominance within three to five years is not realistic. What JSR can and likely will do is establish itself as the standard for a growing segment of TypeScript-first packages and gradually improve the quality floor across that segment through its scoring and documentation systems.
Methodology
This article draws on:
- JSR documentation (jsr.io/docs) — publishing workflow, scoring system, npm compatibility
- JSR package scoring algorithm documentation
- Deno team blog posts on JSR's design decisions
- Node.js 22 release notes on synchronous require() of ESM
- Sigstore documentation on package provenance attestation
- npm registry statistics (packages count, weekly downloads)
- Community comparison posts and JSR early adopter reports