Ranges decide which code you actually run after the next install.
You know ^1.4.2 and ~1.4.2. npm accepts the full node-semver grammar:
- X-ranges:
1.xor1(any 1.y.z),*(anything). - Comparators:
>=1.2.0 <1.5.0; combine with spaces (AND) and||(OR):^2.0.0 || ^3.0.0. - Hyphen ranges:
1.2.3 - 1.4means>=1.2.3 <1.5.0. - Prereleases like
2.0.0-beta.3sort before2.0.0and are only matched by ranges that mention a prerelease of the samemajor.minor.patch.^1.0.0never installs2.0.0-beta.1.
Dist-tags are named pointers to versions. latest is what npm install pkg gives you; maintainers publish betas under tags like next or beta so they do not become latest. Install one with npm install react@next, inspect with npm view react dist-tags.
Pinning: npm install -E pkg (--save-exact) writes 1.4.2 instead of ^1.4.2; npm config set save-exact true makes it the default. Since the lockfile already pins your installed tree, exact ranges mainly matter for tools whose updates you want to adopt deliberately, and for applications that prefer explicit upgrade PRs from bots like Renovate or Dependabot.
Libraries should use ranges (usually ^) so consumers can deduplicate shared dependencies.
Example
npx semver -r "^1.4.2" 1.4.1 1.9.0 2.0.0 # 1.9.0
npx semver -r "1.2.3 - 1.4" 1.2.3 1.4.9 1.5.0 # 1.2.3, 1.4.9
npx semver -r "^2.0.0 || ^3.0.0" 2.5.0 3.1.0 4.0.0 # 2.5.0, 3.1.0
npx semver -r "^1.0.0" 2.0.0-beta.1 # (nothing: prerelease excluded)
npm view vite dist-tags # { latest: '8.x.y', beta: '...', ... }
npm install vite@beta # install whatever "beta" points to
npm install -E typescript@5.9.3 # writes "typescript": "5.9.3"
npm install 'lodash@>=4.17.21 <5'Remember
- Ranges: ^, ~, x-ranges, comparators,
||and hyphen ranges - Prereleases match only ranges that opt into them
- Dist-tags (
latest,next,beta) are movable pointers -Epins exactly; libraries should keep ranges
Watch out for
- Expecting
^1.0.0to pick up2.0.0-rc.1; prereleases of other versions never match.