vlt / docs

  • PricingBenchmarks (opens in new window)Community (opens in new window)Feedback
  • Overview
  • bugs
  • build
  • cache
  • ci
  • config
  • create
  • deploy
  • docs
  • exec-cache
  • exec-local
  • exec
  • help
  • init
  • install
  • list
  • login
  • logout
  • outdated
  • pack
  • ping
  • pkg
  • publish
  • query
  • registry
  • repo
  • run-exec
  • run
  • setup
  • skills
  • token
  • uninstall
  • version
  • view
  • whoami
  • What is a Package?
  • Basic Usage
  • Install all dependencies
  • Adding new packages
  • Registry packages
  • Aliases
  • Git repositories
  • Tarballs
  • Local directories
  • Workspaces
  • Options
  • Dependency type flags
  • Config options
  • Lockfile options
  • Script execution
  • Agent skills
  • Registry options
  • Workspace options
  • Global options
  • Global installs
  • Updating vlt
  • Phased Package Installations
  • Graph Modifiers
  • Override all instances
  • Override transitive dependencies
  • Peer Dependencies
  • Notes
  • Local directories (Linked Folders)
  • Installing from within a Linked Folder
  • Relative paths in Linked Folders
  • Aliases
  1. Client
  2. /
  3. Commands
  4. /
  5. install

vlt install

Usage:

Terminal
$ vlt install [packages ...]

Install dependencies into node_modules, updating package.json and vlt-lock.json appropriately.

By default, vlt install downloads and extracts packages without executing lifecycle scripts—enhancing security by preventing arbitrary code execution during installation. Use vlt build after installation to run scripts selectively.

What is a Package?

  • A folder containing a package.json file
  • A gzipped tarball containing a folder with a package.json file
  • A URL that resolves to a gzipped tarball
  • A <name>@<version> that is published to a configured registry
  • A <name>@<semver-range> that is published to a configured registry
  • A <name>@<tag> that points to a package in a configured registry
  • A <name> by itself, which defaults to using the latest tag from a registry
  • A <custom>:<name>@<version> that points to a custom registry configured in vlt.json
  • A registry:URL#<name>@<version> that points to a custom registry url
  • A <git remote url> that resolves to a folder with a package.json file

Basic Usage

Install all dependencies

Terminal
$ vlt install

Installs all dependencies from package.json without running scripts.

Adding new packages

Using positional arguments that use the valid package specification described above.

Registry packages

Installing by name (a bare spec) requires a registry to be configured — the npm alias has no built-in URL. Run vlt setup once to configure your account's registries. vlt install (and vlt update, vlt uninstall, vlt ci, vlx/vlt exec) require registries.npm specifically — or, if you changed default-registry-alias, the alias it names — and fail with a config error when it is missing.

Terminal
# Latest version
$ vlt install express

# Scoped packages using semver ranges

$ vlt install @vltpkg/graph@^1.0.0

Aliases

Install multiple versions of the same package side-by-side:

Terminal
# Install react v18 as my-react
$ vlt install my-react@npm:react@18

# Install both major versions under aliased names

$ vlt install react18@npm:react@18 react19@npm:react@19

In the vlt cli the traditional aliases also enable installing from custom registries:

Terminal
# Install from vlt.json configured custom registry
$ vlt install custom:@org/pkg@^1.0.0

# Install using an aliased name, pointing to a custom registry

$ vlt install pkg@custom:@org/pkg

Git repositories

Terminal
# GitHub shorthand
$ vlt install github:user/repo
$ vlt install github:user/repo#v1.0.0
$ vlt install github:user/repo#semver:^1.0

# Full git URL

$ vlt install git+ssh://git@github.com:user/repo.git

# OR

$ vlt install git+https://github.com/user/repo.git#main

# GitLab

$ vlt install gitlab:user/repo

# Bitbucket

$ vlt install bitbucket:user/repo

When using #semver:<range>, vlt looks for matching tags/refs in the remote repository.

Tarballs

Terminal
# Local tarball
$ vlt install ./package.tgz

# Remote tarball

$ vlt install https://example.com/package.tgz

Local directories

Terminal
# Relative path
$ vlt install ./my-local-package

# Install from parent directory

$ vlt install ../shared-lib

Workspaces

When a name with no range resolves to a workspace, vlt links it and saves workspace:* to package.json. Use my-lib@workspace:* to require the workspace.

Terminal
$ vlt install my-lib

Options

Dependency type flags

FlagShortDescription
--save-prod-PAdd to dependencies (default)
--save-dev-DAdd to devDependencies
--save-optional-OAdd to optionalDependencies
--save-peerAdd to peerDependencies
--save-typesAdd matching @types/* to devDependencies

--save-types skips packages that ship their own types:

Terminal
# express ships no types: adds @types/express to devDependencies too
$ vlt install express --save-types

# zod ships its own types: adds zod only

$ vlt install zod --save-types

Config options

FlagDescription
--save-configSave registry and git host options given on the command line or via env to the project vlt.json (user one with --config=user), see Registries

Lockfile options

FlagDescription
--frozen-lockfileFail if lockfile is missing or out of sync with package.json
--expect-lockfileFail if lockfile is missing or outdated
--lockfile-onlyUpdate only vlt-lock.json and package.json, skip node_modules

Script execution

FlagDescription
--allow-scripts=<selector>Allow specific packages to run lifecycle scripts during install; saved to vlt.json
Terminal
# Allow specific packages to run scripts during install
$ vlt install --allow-scripts="#esbuild, #node-gyp"

# Allow only direct dependencies to run scripts

$ vlt install --allow-scripts=":root > *"

vlt install and vlt update save --allow-scripts (or VLT_ALLOW_SCRIPTS) to the project vlt.json after a successful install. When another query is stored, it is kept and a hint says so: change it with vlt build. --lockfile-only saves nothing. An invalid query fails before anything is installed.

Agent skills

FlagDescription
--allow-skills=<selector>Link the agent skills of matching packages into the agent skill dirs after install (default: none); saved to vlt.json
--skills-dir=<path>Dirs to link agent skills into, replacing detection (--skills-dir= links nothing)
Terminal
# Link the agent skills of direct dependencies
$ vlt install --allow-skills=":root > *"
# Link one skill of one package
$ vlt install --allow-skills="#agent-browser:skill(agent-browser)"

See vlt skills.

Registry options

FlagDescription
--tag=<tag>Default dist-tag to use (default: latest)
--before=<date>Install relative to the provided ISO 8601 date
Terminal
# Install from beta channel
$ vlt install express --tag=beta

# Install versions published before a specific date

$ vlt install lodash --before="2024-01-01"

Workspace options

FlagShortDescription
--workspace=<ws>-wLimit operation to specific workspaces
--workspace-group=<group>Operate on named workspace groups
--no-migrate-workspacesDon't save workspaces to vlt.json
Terminal
# Install in specific workspace
$ vlt install -w packages/core lodash

# Install in workspace group

$ vlt install --workspace-group apps express

Global options

FlagShortDescription
--global-gInstall into the global project, see Global installs
--global-dir=<path>Location of the global project (default: vlt/global in the XDG data dir)
--global-bin-dir=<path>Where bins of global packages are linked (default: next to vlt)

Global installs

Install command line tools once, for use anywhere, with -g:

Terminal
$ vlt install -g eslint

# install a local tool

$ vlt install -g ./my-tool

For a walkthrough and how to version your global tools, see Global Installs and its best practices.

Global packages live in a regular vlt project at --global-dir (vlt/global in the XDG data directory by default, e.g. ~/.local/share/vlt/global on Linux):

  • each package gets its own workspace, packages/<name>-ws (packages/@scope+name-ws for scoped packages), with the package as its only dependency
  • the project has its own vlt-lock.json and node_modules
  • bins of global packages are linked next to vlt itself (the folder your shell found vlt in), so they run right away. If that folder isn't writable, vlt uses ~/.local/bin when it's in your PATH (not on Windows), else (or when vlt wasn't run from your PATH) <global-dir>/bin, and prints the line that adds it to your PATH. Set --global-bin-dir to pick the folder. Or run vlt setup --path (vlt setup offers it at its end); in a terminal, vlt install -g offers to do it once (answer n and it won't ask again). --yes adds it without asking.

The current project is never read or changed by a global install, including its vlt.json. global, global-dir and global-bin-dir can be set on the command line, via env (VLT_GLOBAL, VLT_GLOBAL_DIR, VLT_GLOBAL_BIN_DIR) or in the user config.

With Node.js from the nodejs.org macOS installer, vlt is in a root-owned /usr/local/bin, so bins usually go to ~/Library/Application Support/vlt/global/bin. Run vlt setup --path, use Node.js from Homebrew, nvm or fnm, or set --global-bin-dir to a folder in your PATH. A --global-bin-dir with spaces needs no escaping in the user config (quote it on the command line as usual).

A package name with no version installs the latest dist-tag (or --tag), like npm install -g, so re-running it upgrades. Use a version or range to pin one instead.

Terminal
# upgrade to latest
$ vlt install -g eslint

# pin a version range

$ vlt install -g eslint@9

# list global packages

$ vlt ls -g

# run their install scripts

$ vlt build -g

# remove a global package and its bins

$ vlt uninstall -g eslint

Running vlt install -g with no packages re-installs the global project and re-links all bins into the current bin folder, removing the ones vlt linked into folders it used before (e.g. under another Node.js version).

A bin name already in the bin folder (linked by another global package, or a file not created by vlt) is skipped with a warning. Use --force to overwrite it. Outside <global-dir>/bin, --force only replaces links, never plain files or the running vlt.

Like any install, scripts of global packages are not run, use vlt build -g to run them.

--workspace, --workspace-group and --lockfile-only are not supported with -g, and the dependency type flags (--save-dev, --save-types, etc.) are ignored. -g is supported by install, uninstall, list, query and build.

Updating vlt

vlt install -g vlt updates the vlt your shell runs (the one in your PATH), the way it was installed:

  • installed with npm (e.g. by the install script): runs that npm, npm install -g vlt@<version>
  • installed as a global package: re-installs it like any global package
  • installed some other way: says how to update it instead (e.g. brew upgrade vlt)

If vlt can't write where it is installed, it installs itself as a global package instead and links its bins like any other. The old vlt keeps running until that folder comes before it in your PATH.

A bare vlt never downgrades: when latest is older than the running vlt, it says so. Use vlt@latest or a version to install it anyway.

Terminal
$ vlt install -g vlt
$ vlt install -g vlt@latest

Phased Package Installations

Traditional package managers run lifecycle scripts automatically during install—a security risk that allows malicious packages to execute arbitrary code.

vlt separates installation into two distinct phases:

  1. Install phase: Downloads and extracts packages (no scripts)
  2. Build phase: Runs lifecycle scripts for selected packages
Terminal
# Phase 1: Install packages safely
$ vlt install

# Phase 2: Build with control over what runs

$ vlt build

This separation enables you to:

  • Inspect dependencies before any code execution
  • Selectively allow scripts using DSS queries
  • Protect against supply chain attacks

By default, vlt build uses :scripts:not(:built):not(:malware) which excludes packages with known malware alerts. vlt build <query> saves the query as allow-scripts, so later installs run those scripts.

For more details, see the vlt build documentation.

Graph Modifiers

Graph Modifiers provide precise control over dependency resolution using Dependency Selector Syntax (DSS). Configure them in your vlt.json:

vlt.json
{
  "modifiers": {
    "#react": "^19",
    ":root > #webpack > #browserslist": "^4.23.0"
  }
}

Override all instances

Pin a package version across the entire dependency graph:

vlt.json
{
  "modifiers": {
    "#lodash": "^4.17.21"
  }
}

Override transitive dependencies

Target specific dependency paths:

vlt.json
{
"modifiers": {
  ":root > #webpack > #terser-webpack-plugin > #terser": "^5.31.0"
}
}

When multiple selectors match, the most specific one wins.

For more details on Graph Modifiers, see the Graph Modifiers documentation.

Peer Dependencies

vlt handles peer dependencies by isolating their contexts, allowing multiple versions of the same package to coexist when necessary. When different parts of your dependency tree require incompatible peer versions, vlt automatically subdivides the graph into separate peer dependency contexts.

Key behavior: vlt always attempts to use a single version of a package for peer dependencies. When that's not possible, it duplicates packages as needed, storing them in separate locations within node_modules/.vlt/ and linking appropriately so each package resolves to its correct peer version.

This happens automatically during vlt install—no configuration required.

For more details, see the Peer Dependencies documentation.

Notes

  • Running vlt install with no arguments installs all dependencies from package.json
  • The lockfile (vlt-lock.json) ensures reproducible installs across environments
  • Workspaces read from package.json or pnpm-workspace.yaml are saved to vlt.json (not with --frozen-lockfile / --expect-lockfile), see Workspaces
  • Use vlt ci in CI pipelines: removes node_modules, installs exactly from vlt-lock.json (like --expect-lockfile) and, like vlt install, runs no dependency lifecycle scripts unless --allow-scripts is set; run vlt build after to build packages
  • With vlt-lock.json and every registry package cached, --frozen-lockfile makes no registry requests (git deps may still clone). Registry and cache come from vlt.json, VLT_* env vars or flags; .npmrc and npm_config_* aren't read. Telemetry is still sent unless disabled.
  • Peer dependencies are handled automatically with context isolation when version conflicts arise

Local directories (Linked Folders)

Terminal
# Relative path
$ vlt install ./my-local-package

# Install from parent directory

$ vlt install ../shared-lib

Local directories can be symlinked to a project via the file: protocol. Note that these differ from local tarballs (file:./pkg.tgz) which are extracted and treated as immutable.

Installing from within a Linked Folder

When you cd into a linked folder and run vlt install, the vlt cli allows you to add dependencies to that folder's package.json:

Terminal
# Project has: "my-lib": "file:./libs/my-lib"

$ cd libs/my-lib $ vlt install lodash@^4.17.0

# Adds lodash to libs/my-lib/package.json

vlt detects your current working directory relative to the project root and targets the appropriate package.json.

Note: to use vlt install or vlt uninstall to manage dependencies of a local linked directory, that folder needs to be already in the chain of dependencies of either the root project or one of its workspaces in order for the vlt cli to properly manage the operation.

Relative paths in Linked Folders

File specs resolve relative to the linked folder's location:

Terminal
# From libs/my-lib, reference ../shared
$ cd libs/my-lib
$ vlt install file:../shared

Aliases

Text
i, add

PreviousinitNextlist

On this page

  • What is a Package?
  • Basic Usage
  • Install all dependencies
  • Adding new packages
  • Registry packages
  • Aliases
  • Git repositories
  • Tarballs
  • Local directories
  • Workspaces
  • Options
  • Dependency type flags
  • Config options
  • Lockfile options
  • Script execution
  • Agent skills
  • Registry options
  • Workspace options
  • Global options
  • Global installs
  • Updating vlt
  • Phased Package Installations
  • Graph Modifiers
  • Override all instances
  • Override transitive dependencies
  • Peer Dependencies
  • Notes
  • Local directories (Linked Folders)
  • Installing from within a Linked Folder
  • Relative paths in Linked Folders
  • Aliases
Edit this page

Deploy your package on vlt.io

Publish scoped and private packages, manage organizations and access, and give every developer and CI environment a consistent source for public and private JavaScript dependencies.

Publish now