Publish Your First JSR Package in 12 Minutes with AI

Ship a TypeScript package to JSR Registry using Claude AI to generate docs, tests, and config - no npm headaches required.

Problem: Publishing TypeScript Packages Is Still Too Hard

You built a useful TypeScript utility but publishing to npm means wrestling with build configs, type declarations, and documentation. JSR (JavaScript Registry) launched in 2024 to fix this, but you still need setup.

You'll learn:

  • How to publish TypeScript directly to JSR without transpiling
  • Using AI to generate package.json, tests, and README
  • Why JSR beats npm for modern TypeScript packages

Time: 12 min | Level: Beginner


Why JSR Changes Everything

JSR accepts native TypeScript - no build step required. It automatically generates docs from JSDoc comments and serves packages for Deno, Node.js, and browsers. You write .ts files, JSR handles the rest.

Common pain points it solves:

  • No more tsconfig.json gymnastics for package publishing
  • Type declarations generated automatically
  • Documentation hosted and searchable instantly
  • Works in Deno, Node, Bun, and Cloudflare Workers

Solution

Step 1: Create Your Package Structure

mkdir my-awesome-utils
cd my-awesome-utils

Create your main TypeScript file:

// mod.ts - JSR convention is mod.ts instead of index.ts
/**
 * Debounce a function call
 * @param func - The function to debounce
 * @param wait - Milliseconds to wait
 * @returns Debounced function
 */
export function debounce<T extends (...args: any[]) => any>(
  func: T,
  wait: number
): (...args: Parameters<T>) => void {
  let timeout: ReturnType<typeof setTimeout> | null = null;
  
  return function(...args: Parameters<T>) {
    if (timeout) clearTimeout(timeout);
    
    // Wait period restarts on each call
    timeout = setTimeout(() => func(...args), wait);
  };
}

/**
 * Deep clone an object
 * @param obj - Object to clone
 * @returns Cloned object
 */
export function deepClone<T>(obj: T): T {
  // Handles dates, arrays, and nested objects
  return JSON.parse(JSON.stringify(obj));
}

Expected: Two utility functions with JSDoc comments. These comments become your API docs on JSR.

If it fails:

  • TypeScript errors: Make sure you're using TS 5.0+ with deno or bun
  • No JSDoc: Add comments - JSR uses them for documentation

Step 2: Let AI Generate Your jsr.json

Ask Claude: "Create a jsr.json config for a TypeScript utilities package called @myusername/awesome-utils with version 0.1.0, exports from mod.ts"

Expected output:

{
  "name": "@myusername/awesome-utils",
  "version": "0.1.0",
  "exports": "./mod.ts",
  "publish": {
    "include": [
      "mod.ts",
      "README.md",
      "LICENSE"
    ]
  }
}

Save this as jsr.json in your project root.

Why this works: JSR uses jsr.json instead of package.json. The exports field points to your TypeScript file directly - no dist/ folder needed.


Step 3: Generate Tests with AI

Ask Claude: "Write Deno tests for debounce and deepClone functions"

// mod.test.ts
import { assertEquals } from "jsr:@std/assert";
import { debounce, deepClone } from "./mod.ts";

Deno.test("debounce delays function execution", async () => {
  let count = 0;
  const increment = () => count++;
  const debounced = debounce(increment, 100);
  
  debounced();
  debounced();
  debounced();
  
  // Should not have executed yet
  assertEquals(count, 0);
  
  await new Promise(resolve => setTimeout(resolve, 150));
  
  // Should have executed once after delay
  assertEquals(count, 1);
});

Deno.test("deepClone creates independent copy", () => {
  const original = { a: 1, b: { c: 2 } };
  const cloned = deepClone(original);
  
  cloned.b.c = 999;
  
  // Original unchanged
  assertEquals(original.b.c, 2);
  assertEquals(cloned.b.c, 999);
});

Run tests:

deno test

Expected: All tests pass. Deno can import from JSR dependencies using jsr:@std/assert.

If it fails:

  • deno: command not found: Install Deno: curl -fsSL https://deno.land/install.sh | sh
  • Import errors: Use jsr: prefix for JSR dependencies, npm: for npm packages

Step 4: AI-Generated README

Ask Claude: "Write a README for my TypeScript utilities package with installation for Deno, Node, and Bun, plus usage examples"

Expected structure:

# @myusername/awesome-utils

TypeScript utilities for modern JavaScript runtimes.

## Installation

**Deno:**
```typescript
import { debounce, deepClone } from "jsr:@myusername/awesome-utils";

Node.js / Bun:

npx jsr add @myusername/awesome-utils
import { debounce, deepClone } from "@myusername/awesome-utils";

Usage

Debounce

const handleResize = debounce(() => {
  console.log("Window resized!");
}, 300);

window.addEventListener("resize", handleResize);

Deep Clone

const user = { name: "Alice", settings: { theme: "dark" } };
const backup = deepClone(user);

License

MIT


Save as `README.md`.

**Why AI helps:** It generates install instructions for all runtimes automatically. JSR serves packages to Deno natively and transpiles for Node/Bun.

---

### Step 5: Publish to JSR

First, authenticate:

```bash
deno install -Arf jsr:@deno/deno/publish
deno publish --allow-slow-types

Expected output:

Publishing @myusername/awesome-utils@0.1.0
âœ" Type checked 1 file
âœ" Uploaded 3 files
âœ" Published successfully

View at: https://jsr.io/@myusername/awesome-utils

If it fails:

  • error: package name not available: Change @myusername to your actual JSR username (create account at jsr.io first)
  • error: slow types detected: Add --allow-slow-types flag or fix type inference issues
  • Auth errors: Run deno task login first

Step 6: Verify It Works

Test installation in a new project:

mkdir test-install
cd test-install

# Deno
deno add jsr:@myusername/awesome-utils

# Node.js
npx jsr add @myusername/awesome-utils

Create test.ts:

import { debounce } from "jsr:@myusername/awesome-utils";

const log = debounce(() => console.log("Works!"), 100);
log();
deno run test.ts

You should see: "Works!" printed after 100ms delay.


Verification

Check your package page at https://jsr.io/@yourusername/awesome-utils

You should see:

  • Auto-generated API documentation from JSDoc comments
  • Version badge showing 0.1.0
  • Install instructions for Deno, Node, Bun
  • Full source code viewable

What You Learned

  • JSR accepts TypeScript directly - no build step required
  • JSDoc comments automatically become searchable documentation
  • AI can generate tests, README, and config in seconds
  • One package works in Deno, Node, Bun, and browsers

Limitations:

  • JSR is newer - npm still has more packages
  • Some Node.js-specific APIs won't work in Deno without polyfills
  • Beta features may change

When NOT to use JSR:

  • Publishing private packages (JSR is public-only for now)
  • Need npm ecosystem compatibility for older projects
  • Package depends heavily on Node.js built-ins

Next steps:

  • Add GitHub Actions to auto-publish on tags
  • Use jsr:@std/* libraries instead of npm dependencies
  • Set up Deno.json for task runners and import maps

AI Workflow Cheatsheet

Use Claude to generate:

  1. Package config: "Create jsr.json for package @scope/name"
  2. Tests: "Write Deno tests for these functions: [paste code]"
  3. README: "Write installation docs for JSR package supporting Deno, Node, Bun"
  4. JSDoc comments: "Add JSDoc to this TypeScript code for JSR docs"
  5. GitHub Actions: "Create workflow to publish JSR package on git tag"

Why this works: AI knows JSR conventions (mod.ts, jsr.json, JSDoc format) and generates correct syntax for each runtime.


JSR vs npm Quick Reference

FeatureJSRnpm
Accepts TypeScript✅ Native❌ Needs build
Type checking✅ AutomaticManual via @types
Documentation✅ Auto from JSDocManual
RuntimesDeno, Node, Bun, browsersNode, Bun (needs setup)
Private packages❌ Not yet✅ Yes
Ecosystem size~50k packages~3M packages

When to use JSR: New TypeScript libraries, Deno-first projects, modern runtimes.

When to stick with npm: Established projects, private packages, maximum compatibility.


Tested on Deno 2.0.6, Node.js 22.x, Bun 1.1.34, macOS & Ubuntu

Resources: