Deno 2.0: Node compatibility without losing identity
Updated: 2026-07-07
Deno 2.0 brings Node compatibility with npm and package.json while maintaining its unique identity. Explore how to use the runtime in existing projects.
Deno 2.0 shipped in October 2024, and five months later it is a good time to take stock. Version 2.0 looks nothing like the original 2018 Deno, which was born as an explicit rejection of many Node.js decisions. What Ryan Dahl and his team have done is probably the most pragmatic move they could: accept that npm won, integrate with the existing ecosystem, and stop fighting package.json and node_modules. Without losing, of course, the pieces that have always made Deno different.
Key takeaways
-
Deno 2 understands
package.json, createsnode_modules, and works with pnpm: the barrier to existing projects has fallen drastically. -
The permissions model remains unique: sandbox by default, no network or disk without explicit flags.
-
Native TypeScript without tsc or tsx remains a real advantage over Node.
-
Node compatibility is better than ever but not total: large frameworks (Next.js, NestJS) remain imperfect.
-
The 2025 JavaScript ecosystem has three serious runtimes: Node (mature default), Bun (raw speed), Deno (security and DX).
What 2.0 actually brings
Node compatibility. Deno 2 supports the npm: protocol for package resolution, and now also understands package.json, respects declared dependencies, creates and reads node_modules, and works with pnpm as a package manager. deno install in an existing Node project works. deno run script.js on a file using require('express') works. A year ago this was unthinkable. The official release post walks through all the details: Deno 2.0 launch blog[1].
Native workspaces. Deno 2.0 natively supports monorepos with multiple packages, sharing configuration and dependencies. For teams operating a monorepo structure, this was a hard requirement — its absence in 1.x was a real barrier.
JSR. The official registry that Deno, TypeScript, and several ecosystem actors have started pushing as a modern npm alternative. JSR[2] accepts packages directly in TypeScript, resolves types automatically, publishes ESM by default, and supports strict semantic versioning. A technically compelling proposal, but adoption is still small; for most projects, npm remains the primary source.
Closed Node API gaps. process.env, parts of fs, and better worker support. The known gaps have been almost entirely filled.
What problem it solves in practice
The concrete problem Deno 2 solves: the barrier to trying Deno in an existing project was too high. Before, migrating an Express app to Deno meant rewriting imports, managing types by hand, and wrestling with odd transitive dependencies. Today the experiment is opening an existing project, running deno run main.ts, and seeing what happens.
This changes the conversation. Deno 1.x was an all-or-nothing bet: either rewrite in its style, or stay in Node. Deno 2.0 makes a gradual upgrade possible: use Deno to run, test, lint, and format your Node project, keeping package.json as source of truth, and migrate to Deno’s own APIs only when it suits you.
Where it still shines
The permissions model is the most visible advantage. Deno runs code in an isolated environment by default: a script cannot read files, open sockets, or execute subcommands unless explicitly permitted via flags (--allow-read, --allow-net, etc.). For running third-party code — migration scripts, generators, build tools — this is a genuine value proposition. In Node, the --experimental-permission module is progressing but remains experimental and less complete. The Deno security documentation[3] covers the full model.
The developer experience remains strong. deno fmt, deno lint, deno test, deno bench, and deno task come built in, with consistent output and no configuration required. They generally work correctly from the first run. In Node the equivalents mean assembling prettier, eslint, vitest or jest, and tsx or ts-node to execute TypeScript.
Standard Web APIs work as in the browser: fetch, Request, Response, URL, crypto.subtle, WebSocket. For code that runs on both server and edge, a genuine advantage. See also our notes on full-stack TypeScript in 2025 for broader runtime context.
Native TypeScript remains unmatched. Deno executes TypeScript without prior compilation — no tsc, no tsx, no configuration.
Where it still falls short
Node compatibility is better but not total. Some popular packages still do not work, especially those with native binaries that expect exactly Node’s node_modules layout and loader. Puppeteer and Playwright work, but with caveats.
Large frameworks (Next.js, NestJS) expect a specific process lifecycle, and while Deno 2.0 makes a serious effort to emulate it, compatibility with major frameworks remains imperfect. For APIs with Express, Fastify, or Hono it works well in almost all cases. For a complex Next.js 14 project, I would stay on Node.
The community and ecosystem are smaller. Finding tutorials, Stack Overflow answers, or Deno-specific examples is harder than for Node. This gap is narrowing, but it is a real cost.
Cold-start performance has improved considerably, but still runs slightly below Bun in benchmarks. For serverless functions where startup time is critical, measure before committing.
Deno vs Bun vs Node
Three serious runtimes in the 2025 JavaScript ecosystem:
-
Node: mature default, stable, full ecosystem support. If you do not have a concrete reason to change, do not change.
-
Bun: bets on raw speed and a very fast package manager. Great for development, and its Node compatibility is solid. Production-ready, but with a shorter track record.
-
Deno: bets on security, developer experience, and standard web APIs. Node compatibility in 2.0 removes the main friction.
My informal recommendation: Small new APIs or infrastructure scripts: Deno 2. Large services with established ecosystems: Node. Local development with fast reload: Bun. Any project where security against third-party code matters: Deno by default.
How to think about the decision
The question Deno 2.0 answers is not "is it better than Node?" but "is it worth trying in my project?". The answer, unlike in 2022, is a qualified yes for many cases:
-
If you have a new project that does not depend on a large Node framework, Deno is a serious option.
-
If you have an existing project and want to try tools (formatter, tests, linter) with less configuration overhead, Deno can coexist without a full rewrite.
-
If your concern is security against third-party dependencies, the permissions model remains unique.
Deno 2.0 is humbler than Deno 1.x. It acknowledges that Node is the standard and that practical life means working within its ecosystem, but it retains its own identity. A runtime has to choose between being pure or being useful, and Deno 2 has chosen useful without giving up its design principles. That is a mature decision.