On this page
Dependency management
Deno is a full package manager for npm and JSR packages, built
into the deno binary. It works with deno.json, package.json, or both at
once, and it is secure by default: npm lifecycle scripts don't run unless you
approve them. This page covers the day-to-day workflow; the
toolbelt table lists every command.
Quick start Jump to heading
In a project that already declares dependencies (in package.json or
deno.json), install them with:
deno install
Add a new dependency from npm or JSR. Unprefixed names are npm packages, like
npm install:
deno install express # any npm package
deno install jsr:@std/assert # a JSR package
The dependency is recorded in your project's config file, and you import it by its bare name:
import express from "express";
import { assertEquals } from "@std/assert";
The dependency toolbelt Jump to heading
Every dependency task has a built-in subcommand. Each links to its full reference:
| Command | What it does |
|---|---|
deno install |
Install the project's dependencies (or a tool, with -g) |
deno add |
Add a dependency to the config file |
deno remove |
Remove a dependency from the config file |
deno outdated |
List dependencies with newer versions |
deno update |
Update dependencies to newer versions |
deno why |
Explain why a package is in your dependency tree |
deno info |
Show a module's full dependency graph and cache details |
deno audit |
Check installed dependencies for known vulnerabilities |
deno approve-scripts |
Approve npm lifecycle scripts for specific packages |
deno ci |
Clean, lockfile-strict install for CI (like npm ci) |
deno publish |
Publish a package to JSR |
deno pack |
Build an npm-compatible tarball from a Deno package |
deno uninstall |
Remove a globally installed tool |
deno clean |
Remove the global module cache |
Choosing versions Jump to heading
It is possible to specify a version range for the package you are importing.
This is done using the @ symbol followed by a version range specifier, and
follows the semver versioning scheme. If you need to
share a single version range across multiple workspace members, see
catalog: for centralized dependency versions.
For example:
@scopename/mypackage # highest version
@scopename/mypackage@16.1.0 # exact version
@scopename/mypackage@16 # highest 16.x version >= 16.0.0
@scopename/mypackage@^16.1.0 # highest 16.x version >= 16.1.0
@scopename/mypackage@~16.1.0 # highest 16.1.x version >= 16.1.0
Here is an overview of all the ways you can specify a version or a range:
| Symbol | Description | Example |
|---|---|---|
1.2.3 |
An exact version. Only this specific version will be used. | 1.2.3 |
^1.2.3 |
Compatible with version 1.2.3. Allows updates that do not change the leftmost non-zero digit. For example, 1.2.4 and 1.3.0 are allowed, but 2.0.0 is not. |
^1.2.3 |
~1.2.3 |
Approximately equivalent to version 1.2.3. Allows updates to the patch version. For example, 1.2.4 is allowed, but 1.3.0 is not. |
~1.2.3 |
>=1.2.3 |
Greater than or equal to version 1.2.3. Any version 1.2.3 or higher is allowed. |
>=1.2.3 |
<=1.2.3 |
Less than or equal to version 1.2.3. Any version 1.2.3 or lower is allowed. |
<=1.2.3 |
>1.2.3 |
Greater than version 1.2.3. Only versions higher than 1.2.3 are allowed. |
>1.2.3 |
<1.2.3 |
Less than version 1.2.3. Only versions lower than 1.2.3 are allowed. |
<1.2.3 |
1.2.x |
Any patch version within the minor version 1.2. For example, 1.2.0, 1.2.1, etc. |
1.2.x |
1.x |
Any minor and patch version within the major version 1. For example, 1.0.0, 1.1.0, 1.2.0, etc. |
1.x |
* |
Any version is allowed. | * |
Inspecting and updating dependencies Jump to heading
See which dependencies have newer versions, then update them:
deno outdated # display what's behind, without changing anything
deno update # update to the latest semver-compatible versions
deno update --latest # ignore semver ranges and go to the newest releases
When you want to know where a package comes from,
deno why prints every dependency path that
leads to it:
deno why npm:kleur
deno info shows a module's full dependency
graph, including sizes and cache locations, for your entry point or any
specifier:
deno info main.ts
deno info jsr:@std/http
Auditing for vulnerabilities Jump to heading
deno audit checks your installed dependencies
against vulnerability databases:
deno audit # report known vulnerabilities
deno audit --level=high # only high and critical severity
deno audit --fix # upgrade affected packages automatically
deno audit --socket # check against the socket.dev database
Auditing is one part of keeping your dependency tree safe over time. See Supply chain management for the wider practices: minimum dependency age, lockfile discipline, and a recommended CI baseline.
Lifecycle scripts Jump to heading
Unlike npm, Deno does not run preinstall/postinstall scripts by default: a
package's install scripts are a common attack vector, so running them is opt-in.
When a dependency genuinely needs its scripts (native addons, for example),
allow it explicitly:
deno install --allow-scripts=npm:better-sqlite3
Or manage approvals interactively with
deno approve-scripts. Scripts only
execute when a node_modules directory is in use.
Lockfile and reproducible installs Jump to heading
Deno automatically maintains a deno.lock file recording the exact version and
integrity hash of every dependency, and verifies it on subsequent runs. Commit
it. In CI and production, install strictly from the lockfile with
deno ci, which errors if deno.lock is missing
or out of date instead of silently resolving new versions:
deno ci # like npm ci
deno ci --prod # additionally skip devDependencies
You can also enforce a frozen lockfile on any command with --frozen, or
configure the lockfile's behavior and path in
deno.json. For why this matters and
what to do when the lockfile fights you, see
Supply chain management.
node_modules and the cache Jump to heading
By default Deno stores dependencies in a global cache and creates a local
node_modules directory only when your project has a package.json. Control
this with the nodeModulesDir option in
deno.json.
By default, Deno uses a global cache directory (DENO_DIR) for downloaded
dependencies. This cache is shared across all projects.
You can force deno to refetch and recompile modules into the cache using the
--reload flag.
# Reload everything
deno run --reload my_module.ts
# Reload a specific module
deno run --reload=jsr:@std/fs my_module.ts
The reverse also works: --cached-only forbids the network entirely and fails
if anything in the dependency tree is not already cached, which is useful for
offline work and reproducible CI:
deno run --cached-only mod.ts
Vendoring remote modules Jump to heading
If your project has external dependencies, you may want to store them locally to avoid downloading them from the internet every time you build your project. This is especially useful when building your project on a CI server or in a Docker container, or patching or otherwise modifying the remote dependencies.
Deno offers this functionality through a setting in your deno.json file:
{
"vendor": true
}
Add the above snippet to your deno.json file and Deno will cache all
dependencies locally in a vendor directory when the project is run, or you can
optionally run the deno install --entrypoint command to cache the dependencies
immediately:
deno install --entrypoint main.ts
You can then run the application as usual with deno run:
deno run main.ts
After vendoring, you can run main.ts without internet access by using the
--cached-only flag, which forces Deno to use only locally available modules.
For more advanced overrides, such as substituting dependencies during development, see Overriding dependencies.
Overriding dependencies Jump to heading
Deno provides mechanisms to override dependencies, enabling developers to use custom or local versions of libraries during development or testing.
Note: If you need to cache and modify dependencies locally for use across builds, consider vendoring remote modules.
Overriding local packages Jump to heading
For developers familiar with npm link in Node.js, Deno provides a similar
feature for local JSR and npm packages through the links field in deno.json.
This allows you to override dependencies with local versions during development
without needing to publish them.
Example:
{
"links": [
"../some-package-or-workspace"
]
}
Key points:
- The
linksfield accepts paths to directories containing packages or workspaces. If you reference a single package within a workspace, the entire workspace will be included. - Both JSR and npm packages are supported.
- This feature is only respected in the workspace root. Using
linkselsewhere will trigger warnings.
Limitations:
- Git-based dependency overrides are unavailable.
- The
linksfield requires proper configuration in the workspace root.
Overriding NPM packages Jump to heading
Deno supports linking npm packages with local versions, similar to how JSR packages can be linked. This allows you to use a local copy of an npm package during development without publishing it.
To use a local npm package, configure the links field in your deno.json:
{
"links": [
"../path/to/local_npm_package"
]
}
This feature requires a node_modules directory and has different behaviors
depending on your nodeModulesDir setting:
- With
"nodeModulesDir": "auto": The directory is recreated on each run, which slightly increases startup time but ensures the latest version is always used. - With
"nodeModulesDir": "manual"(default when using package.json): You must rundeno installafter updating the package to get the changes into the workspace'snode_modulesdirectory.
Limitations:
- Specifying a local copy of an npm package or changing its dependencies will purge npm packages from the lockfile, which may cause npm resolution to work differently.
- The npm package name must exist in the registry, even if you're using a local copy.
Overriding HTTPS imports Jump to heading
Deno also allows overriding HTTPS imports through the scopes field in
deno.json. This feature is particularly useful when substituting a remote
dependency with a local patched version for debugging or temporary fixes.
Example:
{
"imports": {
"example/": "https://deno.land/x/example/"
},
"scopes": {
"https://deno.land/x/example/": {
"https://deno.land/x/my-library@1.0.0/mod.ts": "./patched/mod.ts"
}
}
}
Key points:
- The
scopesfield in the import map allows you to redirect specific imports to alternative paths. - This is commonly used to override remote dependencies with local files for testing or development purposes.
- Scopes apply only to the root of your project. Nested scopes within dependencies are ignored.
Development only dependencies Jump to heading
Sometimes dependencies are only needed during development, for example dependencies of test files or build tools. In Deno, the runtime does not require you to distinguish between development and production dependencies, as the runtime will only load and install dependencies that are actually used in the code that is being executed.
However, it can be useful to mark dev dependencies to aid people who are reading
your package. When using deno.json, the convention is to add a // dev
comment after any "dev only" dependency:
{
"imports": {
"@std/fs": "jsr:@std/fs@1",
"@std/testing": "jsr:@std/testing@1" // dev
}
}
When using a package.json file, dev dependencies can be added to the separate
devDependencies field:
{
"dependencies": {
"pg": "npm:pg@^8.0.0"
},
"devDependencies": {
"prettier": "^3"
}
}
JSR packages in package.json Jump to heading
You can depend on JSR packages directly from package.json using the jsr:
scheme, without needing a separate deno.json:
{
"dependencies": {
"@std/path": "jsr:^1.0.9"
}
}
This works with deno install and brings JSR packages to any project that uses
package.json for dependency management.
Dependency overrides Jump to heading
The overrides field in package.json lets you control transitive dependency
versions throughout your dependency tree. This is useful for applying security
patches, fixing version compatibility issues, or replacing packages:
{
"dependencies": {
"express": "^4.18.0"
},
"overrides": {
"cookie": "0.7.0",
"express": {
"qs": "6.13.0"
}
}
}
In this example, cookie is pinned globally to 0.7.0, while qs is
overridden only when required by express.
Why does Deno not have a devImports field? Jump to heading
To understand why Deno does not separate out dev dependencies in the package manifest it is important to understand what problem dev dependencies are trying to solve.
When deploying an application you frequently want to install only the dependencies that are actually used in the code that is being executed. This helps speed up startup time and reduce the size of the deployed application.
Historically, this has been done by separating out dev dependencies into a
devDependencies field in the package.json. When deploying an application,
the devDependencies are not installed, and only the dependencies.
This approach has shown to be problematic in practice. It is easy to forget to
move a dependency from dependencies to devDependencies when a dependency
moves from being a runtime to a dev dependency. Additionally, some packages that
are semantically "development time" dependencies, like (@types/*), are often
defined in dependencies in package.json files, which means they are
installed for production even though they are not needed.
Deno offers two approaches for installing production-only dependencies:
deno install --prod— skipsdevDependenciesfrompackage.json. You can also pass--skip-typesto additionally exclude@types/*packages.deno install --entrypoint— installs only the dependencies that are actually (transitively) imported by the specified entrypoint file. When combined with--prod, type-only dependencies are also excluded from the module graph.
See the deno install reference for more
details.
HTTPS imports Jump to heading
Deno can import modules directly from https: URLs, either inline or mapped in
deno.json. This suits small single-file scripts, but registries (JSR, npm) are
recommended for applications: HTTPS imports can drift to different versions
across files, aren't managed by deno add/deno install, and trust the serving
host. To pin and localize them, see vendoring.
Go deeper Jump to heading
- Publishing packages: JSR, npm via
deno pack, and choosing a registry. - Supply chain management: lockfile discipline, minimum dependency age, audit workflows, and a CI baseline.
- Private repositories:
authenticate remote module hosts with
DENO_AUTH_TOKENS. For private npm registries, see npm support.