Skip to main content

JSR vs npm: JavaScript Package Registries 2026

·PkgPulse Team
0

JSR vs npm: JavaScript Package Registries in 2026

TL;DR

npm is the world's largest software registry — 3M+ packages, universal compatibility, and the default for every JavaScript runtime. JSR (JavaScript Registry) is Deno's TypeScript-first registry that solves real problems npm has: immutable package versions, auto-generated documentation from TypeScript sources, native ESM, and cross-runtime support (Deno, Node.js, Bun, browsers). JSR is not a replacement for npm — it's complementary. Use npm for the existing ecosystem; consider JSR for new packages that should be TypeScript-first and cross-runtime. In 2026, JSR is gaining real traction with the Deno and Hono ecosystems.

Key Takeaways

  • npm registry: 3.2M+ packages — dwarfs every alternative
  • JSR launched in March 2024 — growing rapidly with 40k+ packages by early 2026
  • JSR packages are immutable — published versions can never be modified or deleted (unlike npm where you can unpublish within 72 hours)
  • JSR auto-generates documentation from TypeScript source — no manual doc comments required
  • JSR uses jsr: imports natively in Deno; maps to npm in Node.js/Bun via a special registry
  • JSR scores packages on a scale — TypeScript support, documentation, cross-runtime compatibility
  • npm provenance attestations (GitHub Actions integration) — npm now has similar trust guarantees for new packages

The Problem With npm in 2026

npm is ubiquitous but has accumulated technical debt:

  • CommonJS by default — many packages still ship CJS, causing ESM interop headaches
  • Mutable package versionsnpm unpublish in the first 72 hours, supply chain attacks
  • Types not guaranteed — many packages lack TypeScript types; require @types/* separately
  • No official documentation hosting — you link to an external docs site or just README
  • Package names are squatted — "is-odd", "left-pad", namespace pollution

JSR was designed specifically to solve these. It's not a reaction to npm — it's a deliberate redesign.

Publishing on npm

Package Setup

npm login

# Create package
mkdir my-package && cd my-package
npm init -y

# Set up TypeScript
npm install -D typescript tsup
{
  "name": "@yourorg/my-package",
  "version": "1.0.0",
  "type": "module",
  "main": "./dist/index.cjs",
  "module": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "require": "./dist/index.cjs",
      "types": "./dist/index.d.ts"
    }
  },
  "files": ["dist"],
  "scripts": {
    "build": "tsup src/index.ts --format esm,cjs --dts",
    "prepublishOnly": "npm run build"
  }
}

Publishing

# Build
npm run build

# Check what will be published
npm pack --dry-run

# Publish to npm registry
npm publish --access public

# Publish a beta
npm publish --tag beta

npm Provenance (Supply Chain Security)

# .github/workflows/publish.yml — npm provenance via GitHub Actions
name: Publish to npm
on:
  push:
    tags: ["v*"]

permissions:
  id-token: write  # Required for provenance
  contents: read

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "22"
          registry-url: "https://registry.npmjs.org"

      - run: npm ci
      - run: npm run build

      - name: Publish with provenance
        run: npm publish --provenance --access public
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

Scoped Packages and Organizations

# Create npm organization (npmjs.com)
# Publish scoped package
npm publish --access public  # Public scoped package (free)
npm publish                  # Private scoped package (requires paid plan)

# Deprecate old versions
npm deprecate my-package@1.x "Upgrade to v2"

# Dist tags
npm publish --tag next       # Pre-release
npm dist-tag add my-package@2.0.0 latest  # Promote to latest

Publishing on JSR

Package Setup

# No npm needed — JSR uses jsr.json or package.json "exports"
mkdir my-jsr-package && cd my-jsr-package
// jsr.json
{
  "name": "@yourscope/my-package",
  "version": "1.0.0",
  "exports": {
    ".": "./src/index.ts"  // JSR accepts TypeScript source directly!
  }
}
// src/index.ts — ship TypeScript source directly
// JSR compiles for each target at download time

/**
 * Formats a number as currency.
 *
 * @example
 * ```typescript
 * formatCurrency(1234.56, "USD"); // "$1,234.56"
 * ```
 */
export function formatCurrency(
  amount: number,
  currency: string,
  locale?: string
): string {
  return new Intl.NumberFormat(locale ?? "en-US", {
    style: "currency",
    currency,
  }).format(amount);
}

/**
 * Validates an email address.
 * @returns `true` if valid, `false` otherwise
 */
export function isValidEmail(email: string): boolean {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}

Publishing to JSR

# Install JSR CLI (or use deno)
npm install -g jsr
# Or: npx jsr publish

# Login (uses GitHub OAuth)
jsr whoami

# Publish — JSR reads jsr.json automatically
npx jsr publish

# Dry run to preview
npx jsr publish --dry-run

Auto-Generated Documentation

JSR automatically generates documentation from:

  1. TypeScript type signatures — every exported function, class, interface
  2. JSDoc comments/** ... */ comments become docs
  3. @example blocks — rendered as interactive code examples
  4. Module-level JSDoc — overview documentation
/**
 * A utility library for working with dates.
 *
 * @module
 */

/**
 * Adds days to a date without mutating the original.
 *
 * @param date The starting date
 * @param days Number of days to add (negative to subtract)
 * @returns A new Date instance
 *
 * @example
 * ```typescript
 * const result = addDays(new Date("2026-01-01"), 7);
 * // result is 2026-01-08
 * ```
 */
export function addDays(date: Date, days: number): Date {
  const result = new Date(date);
  result.setDate(result.getDate() + days);
  return result;
}

Result at jsr.io/@yourscope/my-package — fully documented, searchable, with live examples.

Using JSR Packages

In Deno

// deno.json
{
  "imports": {
    "@std/path": "jsr:@std/path@^1.0",
    "@hono/hono": "npm:hono@^4",  // Mix npm and jsr
    "@yourscope/utils": "jsr:@yourscope/utils@^1.0"
  }
}
// Direct import with version pinning
import { formatCurrency } from "jsr:@yourscope/my-package@^1.0";
import { Hono } from "npm:hono";

const app = new Hono();
app.get("/price", (c) => {
  return c.json({ price: formatCurrency(9.99, "USD") });
});

In Node.js / Bun

# JSR packages are available on npm via the @jsr/* namespace
# But prefer using the JSR CLI for proper resolution
npm install jsr
npx jsr add @yourscope/my-package
# → Adds to package.json as "jsr:@yourscope/my-package@^1.0"
# → Creates .npmrc pointing npm to jsr.io
// Node.js usage after `npx jsr add @std/path`
import { join, resolve } from "@std/path";

const fullPath = join("/home/user", "documents", "file.txt");
console.log(resolve(fullPath));

In Browsers

<!-- JSR packages work in browsers via ESM CDN -->
<script type="module">
  import { formatCurrency } from "https://jsr.io/@yourscope/my-package/1.0.0/src/index.ts";
  console.log(formatCurrency(9.99, "USD")); // $9.99
</script>

JSR vs npm Feature Comparison

FeaturenpmJSR
Package count3.2M+40k+ (early 2026)
Package immutability❌ (72hr unpublish)✅ Versions are permanent
TypeScript source❌ (build required)✅ Ship .ts files
Auto-generated docs✅ From types + JSDoc
Publish sizeDistributedSmall (TS source)
ESM-firstOptional✅ Required
Cross-runtimeNode-first✅ Deno/Node/Bun/Browser
Scope requirementOptional✅ Required (@scope/pkg)
Security attestations✅ (Provenance, 2023)✅ Built-in
Slow abuse prevention✅ Score system
Private packages✅ (paid)✅ (free for scopes)
Access control
GitHub Actions integration
PopularityDominantGrowing fast

The Score System

JSR gives each package a score (0–100%) based on:

Documentation       25% — Are all exports documented?
TypeScript          25% — Does it ship TypeScript source?
Description         10% — Is there a package description?
README              10% — Does it have a README?
Runtime compat      30% — Does it work on Deno, Node, Bun, browser?

A score of 100% means the package is:

  • Written in TypeScript (source)
  • All exports have JSDoc
  • Compatible with all major runtimes
  • Has a description and README

This incentivizes quality in ways npm's model doesn't.

Should You Publish on JSR?

Publish on JSR if your package:

  • Is written in TypeScript and targets multiple runtimes
  • Provides utilities that work in Deno, Node.js, Bun, and browsers without modification
  • Benefits from automatic documentation generation
  • Is new and doesn't have npm distribution history to maintain

Keep npm as primary if your package:

  • Depends on Node.js-specific APIs (fs, http, child_process)
  • Has existing npm users and changing the import path would break them
  • Uses CommonJS internally or depends on CJS-only packages
  • Is targeted specifically at Node.js tooling

Publish to both (recommended for new packages):

# Dual publishing is easy — maintain both
jsr.json → publishes TypeScript source to JSR
package.json → publishes compiled output to npm

Ecosystem Adoption in 2026

The JSR ecosystem is growing around Deno's standard library and Hono:

  • @std/ packages* — Deno's standard library now lives on JSR (50+ packages)
  • @hono/hono — Hono is published on both JSR and npm
  • @fresh/* — Deno Fresh framework packages
  • @oak/oak — Oak web framework for Deno

The Deno Company is betting on JSR becoming the standard for cross-runtime TypeScript packages. Whether it challenges npm's dominance remains to be seen — but for new TypeScript-first packages, JSR is worth considering as a primary or dual-publishing target.

Ecosystem & Community Health

npm's community is the entire JavaScript world — over 17 million developers use it regularly, and it processes billions of downloads per week. The tooling around npm is mature and comprehensive: Dependabot, Renovate, npm audit, Snyk, and Socket all operate on the npm registry. npm's package health signals are well-understood: weekly downloads, GitHub stars, open issues, and time since last publish.

JSR's community is smaller but intentionally curated. The Deno team acts as stewards, and the barrier to publishing is slightly higher (requires a scope). This friction is by design — it reduces namespace squatting and junk packages. The JSR Discord and Deno community forums are active, and the Deno Standard Library team maintains a high-quality reference for how packages should be structured.

One meaningful signal of JSR's health: Hono, one of the fastest-growing JavaScript frameworks, publishes on both registries simultaneously. The Hono team treats JSR as a first-class publishing target, not an afterthought. This pattern is increasingly common among TypeScript-first package authors who want to serve both Deno and Node.js users without duplicating their codebase.

For package health monitoring across both registries, tools like PkgPulse aggregate download trends, maintenance signals, and dependency data in one place.

Real-World Adoption

In 2026, JSR adoption is concentrated in three areas. First, the Deno ecosystem — any package that wants to be a first-class Deno citizen publishes on JSR. The Deno runtime itself uses JSR for its standard library, and Deno's import map tooling is optimized for jsr: specifiers. Second, edge runtime packages — Cloudflare Workers, Vercel Edge, and Deno Deploy all benefit from JSR's ESM-first, TypeScript-source approach since they can compile and cache TypeScript at the edge. Third, framework-agnostic utilities — authors who want their package to work in Node.js, Bun, Deno, and browsers without configuration increasingly choose JSR as the publication target because the score system rewards cross-runtime compatibility. For the package manager layer that sits on top of registries, see best JavaScript package managers 2026.

npm's dominance is unquestioned for Node.js-specific tools, frameworks with large plugin ecosystems (Express, Fastify, NestJS), and anything in the React/Vue/Angular ecosystem. The vast majority of developers will never need to think about JSR because npm covers their use case completely.

The dual-publishing pattern — publishing both to npm (compiled) and JSR (TypeScript source) — is the emerging best practice for new utility libraries in 2026. It adds roughly 5 minutes to your release process and ensures your package is accessible to every runtime without compromise. For fast-growing packages taking advantage of JSR's modern features, see 20 fastest growing npm packages 2026.

Developer Experience Deep Dive

The JSR publishing workflow is genuinely simpler than npm's for TypeScript packages. On npm, publishing TypeScript requires a build step: you run tsup or tsc, verify the dist folder, check that exports are correctly mapped, publish, and then pray that the .d.ts files are in the right place. On JSR, you ship the TypeScript source directly — no build step, no dist folder, no exports mapping complexity. JSR handles compilation on its side.

The documentation generation is a standout feature. After publishing to JSR, your package immediately has a polished documentation page at jsr.io/@yourscope/package-name. This page shows every exported function with its type signature, JSDoc description, and rendered @example blocks. For npm, you're linking to a GitHub README or a separate docs site you have to build and maintain yourself.

JSR's score system creates accountability that npm lacks. When a package scores 60% because 40% of its exports aren't documented, that's visible to users before they install it. This subtle pressure improves average package quality across the registry.

On the npm side, the developer experience for TypeScript packages improved dramatically with the exports field stabilization in Node.js 12+ and TypeScript 4.7. Tools like tsup, unbuild, and pkgroll make dual CJS/ESM builds routine. The npm pack --dry-run preview workflow is excellent for catching publish mistakes. And npm's provenance attestations (which link package publishes to specific GitHub Actions runs) give users strong supply chain guarantees.

Security Comparison

Supply chain security is a serious concern in both registries. npm's history includes high-profile incidents: the event-stream compromise in 2018, the colors and faker sabotage in 2022, and numerous typosquat campaigns. npm has responded with provenance attestations, improved malware detection, and namespace protection for popular packages.

JSR's security model has structural advantages. Version immutability means a published version can never be tampered with — once @scope/pkg@1.0.0 is published, those bytes are permanent. npm's 72-hour unpublish window has been exploited in left-pad-style incidents. JSR also requires GitHub OAuth for publishing, which provides a real identity anchor, and the scope requirement prevents the flat namespace squatting problem that plagues npm.

That said, JSR's smaller size means its malware detection and security monitoring are less mature than npm's. npm has years of investment in automated security scanning and the Security Working Group actively monitors the registry. For security-critical applications in 2026, both registries require proper lockfile practices and dependency auditing regardless of which registry you use.

Migration Guide

If you have an existing npm package and want to add JSR support, the migration is additive — you don't remove npm publishing, you add JSR alongside it.

The key steps are: add a jsr.json file at the root pointing to your TypeScript source (not your dist), ensure your TypeScript source is self-contained without Node.js-specific globals, run npx jsr publish --dry-run to check the output, verify your package scores well on jsr.io by reviewing the score breakdown, and then add npx jsr publish to your CI/CD pipeline alongside your existing npm publish step.

Common pitfalls include forgetting to add JSDoc to exported functions (which drops your Documentation score), using process.env without a process polyfill (JSR packages should use Deno.env or a compatibility shim for cross-runtime support), and using Node.js-only modules like fs without appropriate guards.

If you're starting from scratch and targeting the Deno ecosystem primarily, start with JSR and add npm publishing as a secondary step. The JSR tooling is optimized for this direction.

Final Verdict 2026

npm remains the default for the vast majority of JavaScript development. The 3.2 million package ecosystem, universal runtime support, and decades of tooling investment mean you should publish to npm unless you have a specific reason not to.

JSR is the right choice for TypeScript-first packages targeting multiple runtimes. If you're writing a utility that should work identically in Deno, Bun, Node.js, and browsers — and you want automatic documentation and version immutability — JSR is the better publishing target. Dual publishing to both registries is the pragmatic path for 2026.

The JSR vs npm question is not a competition that npm will lose anytime soon. JSR is filling a real gap in the TypeScript-first, cross-runtime package space. In 2026, JSR is where the Deno ecosystem lives and where TypeScript package authors who care deeply about documentation and cross-runtime compatibility are publishing. For everything else, npm.

Methodology

Data sourced from official npm registry (npmjs.com), JSR registry (jsr.io), GitHub repositories, and official documentation. Package counts as of early 2026. JSR score system documented from official JSR documentation. Publishing workflows verified against current CLI versions: npm 10.x, jsr CLI 0.5.x, Deno 2.1.

Related: Best JavaScript Package Managers 2026 for pnpm vs npm vs Yarn comparisons, Best Monorepo Tools 2026 for workspace management across registries, and Bun vs Deno 2 vs Node 22 for JavaScript runtime comparisons.

The 2026 JavaScript Stack Cheatsheet

One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly.