Deno has native support for npm packages without requiring Node.js or npm to be installed. You can use millions of npm packages directly in your Deno projects.
Basic Usage
Direct npm Imports
Import npm packages directly using the npm: specifier:
import express from "npm:express@^4.18.2" ;
import chalk from "npm:chalk@^5.3.0" ;
import { z } from "npm:zod@^3.22.0" ;
const app = express ();
app . get ( "/" , ( req , res ) => {
res . send ( chalk . blue ( "Hello from Deno with Express!" ));
});
app . listen ( 3000 );
Using Import Maps
For cleaner imports, add packages to your deno.json:
{
"imports" : {
"express" : "npm:express@^4.18.2" ,
"chalk" : "npm:chalk@^5.3.0" ,
"zod" : "npm:zod@^3.22.0"
}
}
import express from "express" ;
import chalk from "chalk" ;
import { z } from "zod" ;
Adding npm Packages
Use the deno add command:
# Add npm package
deno add npm:express
# Add with specific version
deno add npm:[email protected]
# Add multiple packages
deno add npm:express npm:cors npm:dotenv
Package Resolution
Deno resolves npm packages using the following logic:
Checks deno.json imports
Downloads from npm registry
Caches in global cache directory
Optionally creates node_modules (if configured)
Global Cache
By default, npm packages are cached globally:
# View cache info
deno info npm:express
# Cache a package
deno cache npm:[email protected]
# Clear cache
rm -rf $( deno info --json | jq -r .npmCache )
node_modules Directory
For better compatibility with npm tooling, enable a local node_modules:
{
"nodeModulesDir" : "auto"
}
Modes:
"auto" - Automatically create when npm packages are used
"manual" - Only use existing node_modules
"none" - Never use node_modules (default)
CommonJS Compatibility
Deno automatically handles CommonJS modules:
// Works with CJS packages
import express from "npm:express" ;
import _ from "npm:lodash" ;
// ESM packages preferred when available
import lodashEs from "npm:lodash-es" ;
Deno prefers ES modules. If a package exports both CommonJS and ESM, Deno will use the ESM version.
TypeScript Types
Automatic Type Resolution
Deno automatically loads types from:
Package’s built-in types
@types/* packages from npm
TypeScript’s type resolution
// Types loaded automatically
import express from "npm:express" ;
import type { Request , Response } from "npm:express" ;
Manual Type Imports
If automatic resolution fails, import types explicitly:
// @deno-types="npm:@types/lodash@^4.14.0"
import _ from "npm:lodash" ;
Or add to deno.json:
{
"compilerOptions" : {
"types" : [ "npm:@types/node" , "npm:@types/express" ]
}
}
Lifecycle Scripts
Some npm packages run scripts during installation. Control this with allowScripts:
{
"allowScripts" : false
}
Or allow specific packages:
{
"allowScripts" : {
"allow" : [
"npm:puppeteer" ,
"npm:esbuild" ,
"npm:playwright"
],
"deny" : [ "npm:suspicious-package" ]
}
}
Lifecycle scripts can execute arbitrary code. Only allow scripts from trusted packages.
Built-in Node Modules
Deno provides compatibility for Node.js built-in modules. See the Node.js Compatibility guide for details.
Package Compatibility
Well-Supported Packages
Utilities
lodash / lodash-es
date-fns
zod
chalk
Testing
vitest
playwright
puppeteer
Build Tools
esbuild
typescript
prettier
Known Limitations
Some packages may not work due to:
Native Node.js APIs not yet supported
Dynamic require() patterns
Bundler-specific features
Binary native modules
Subpath Imports
Access package subpaths directly:
// Main export
import _ from "npm:lodash@^4.17.21" ;
// Subpath
import debounce from "npm:lodash@^4.17.21/debounce" ;
// React example
import React from "npm:react@^18.2.0" ;
import { renderToString } from "npm:react-dom@^18.2.0/server" ;
Peer Dependencies
Deno handles peer dependencies automatically:
{
"imports" : {
"react" : "npm:react@^18.2.0" ,
"react-dom" : "npm:react-dom@^18.2.0"
}
}
Both packages will share the same React version.
Development vs Production
Development
Use version ranges for flexibility:
{
"imports" : {
"express" : "npm:express@^4.18.0" ,
"zod" : "npm:zod@^3.22.0"
}
}
Production
Pin exact versions:
Real-World Example: Express Server
import express from "npm:express@^4.18.2" ;
import cors from "npm:cors@^2.8.5" ;
import { z } from "npm:zod@^3.22.0" ;
const app = express ();
app . use ( cors ());
app . use ( express . json ());
const userSchema = z . object ({
name: z . string (),
email: z . string (). email (),
age: z . number (). min ( 0 ),
});
app . post ( "/users" , ( req , res ) => {
try {
const user = userSchema . parse ( req . body );
res . json ({ success: true , user });
} catch ( error ) {
res . status ( 400 ). json ({ success: false , error });
}
});
app . listen ( 3000 , () => {
console . log ( "Server running on http://localhost:3000" );
});
{
"tasks" : {
"dev" : "deno run --allow-net --watch main.ts"
},
"imports" : {
"express" : "npm:express@^4.18.2" ,
"cors" : "npm:cors@^2.8.5" ,
"zod" : "npm:zod@^3.22.0"
},
"nodeModulesDir" : "auto"
}
Run with:
Debugging npm Packages
Check Package Info
# View package information
deno info npm:express
# See all versions
npm view express versions
Inspect Cache
# View cache directory
deno info --json | jq -r .npmCache
# List cached packages
ls $( deno info --json | jq -r .npmCache )
Common Issues
Ensure the package name and version are correct: deno cache --reload npm:package-name@version
Install type definitions: deno add npm:@types/package-name
Some packages with native binaries may not work. Look for pure JavaScript alternatives.
Enable node_modules mode: { "nodeModulesDir" : "auto" }
Best Practices
Prefer JSR when available
Check if the package or an alternative exists on JSR first.
Use import maps
Add packages to deno.json instead of using direct npm: imports everywhere.
Lock dependencies
Use deno.lock to ensure consistent versions across environments.
Test compatibility
Test npm packages thoroughly before deploying to production.
Monitor security
Review allowScripts permissions and keep dependencies updated.
Migration from Node.js
If migrating from Node.js:
Convert package.json dependencies to deno.json imports
Replace CommonJS require() with ES module import
Update Node.js built-in imports (see Node.js Compatibility )
Test with nodeModulesDir: "auto" if needed
Gradually remove node_modules dependency