npm 11.18.0, published on June 29, 2026, is the release that finishes the work the npm CLI has been doing on isolated installs for the last three and a half years. The headline is PR #9677 (backport of #9674), which graduates --install-strategy=linked from experimental to stable. The mode was added in PR #6078 on January 25, 2023 against npm/rfcs#0042, the isolated-mode RFC that came out of the 2022 Contributor Summit. The promotion removes the implicit "this might eat your node_modules" warning the docs have carried since 2023 and is the closing event of the npm team's longest-running install-strategy effort.
The release is the second feature release of the v11 line this month and ships alongside the npm v12 prerelease the team tagged the same day. The v11 line is the stable line; the v12 prerelease is where the block-unreviewed-install-scripts-by-default change and the linked mode graduation get re-applied against the upcoming major.
What --install-strategy=linked actually does
The default npm install strategy is hoisted. Every transitive dependency is duplicated into the top-level node_modules so any package can require any other transitive package that happens to be hoisted, even when nothing in package.json declares it. That is the root cause of phantom dependencies: code that imports a library it never added to its own dependencies, and the bug only surfaces when a coworker installs with a clean node_modules and the hoisting happens to be different.
--install-strategy=linked (also called isolated mode) installs every package into node_modules/.store/<name>@<version>/node_modules/<dep> and symlinks each package's declared dependencies into its own node_modules/<dep>. The result is that a package can only import what it declared. If it imports an undeclared library, the import fails immediately instead of working by accident and shipping broken.
The npm install docs describe the four strategies this way:
hoisted(default): install non-duplicated at top level, duplicated as necessary within directory structure.nested(formerly--legacy-bundling): install in place, no hoisting.shallow(formerly--global-style): only install direct deps at top level.linked: install innode_modules/.store, link in place, unhoisted.
The release notes for 11.18 add the docs recommendation that made the strategy a real recommendation rather than a hidden CLI flag. The docs page now reads: "We recommend that package authors use --install-strategy=linked during development to catch undeclared (phantom) dependencies before publishing: the isolated layout only exposes a package's declared dependencies, so an import of a package that was never added to package.json can fail instead of resolving by accident and shipping broken. See Catching undeclared (phantom) dependencies."
That sentence was added by PR #9690, the docs change that backports alongside the graduation.
Why the 3.5-year experimental period
Three reasons. First, the experimental flag came with sharp edges. The team's tracking issue #9608 ([Tracking] install-strategy=linked (isolated mode) bugs) lists 19 separate bugs across the install path, the audit path, the npm exec path, and the npm ls path that the 11.18 release closes. The four that the team called out as the worst are audit returning zero results on a real vulnerability (#9609), npm ls reporting false UNMET DEPENDENCY (#9095), napi-postinstall failing to resolve an installed optional native binding (#9620), and the .store layout mismatch with the hidden lockfile (#9612). The 11.18 fixes for each of those are PRs #9638, #9095's follow-on under #9638's logic, #9620's fix under #9665, and #9642 respectively.
Second, the strategy needed an audit story. PR #9625 audits the non-isolated tree under the linked strategy, so npm audit walks both the linked view and the underlying store and reports vulnerabilities accurately. PR #9638 makes the audit report deterministic by re-attaching the dropped via links so two consecutive npm audit runs on an unchanged lockfile produce byte-identical output, which the team's CI infrastructure depends on for diff-based regression tests.
Third, the workspace story. PR #9666 and PR #9665 teach the linked strategy to load transitive optional deps and to apply dev/prod flags correctly inside workspaces; #9648 makes npm exec resolve workspace-local bins; #9669 surfaces undeclared workspaces under the linked strategy so a missing workspaces entry does not silently produce a partial install. Before 11.18 the linked strategy worked for single-package repos but tripped over workspace setups; after 11.18 it is the recommended daily driver for workspaces.
The new npm install-scripts namespace
PR #9635 (backport of #9629) introduces a namespaced npm install-scripts command that owns three subcommands: npm install-scripts approve, npm install-scripts deny, and npm install-scripts ls. The previous npm approve-scripts and npm deny-scripts commands are kept as aliases for one release, then retired. The namespace re-points the install-time, rebuild, and strict-allow-scripts guidance at the new commands so a developer who runs npm install and hits the new npm install blocked N scripts line gets pointed at the same family of commands.
The reason for the namespace is that approve-scripts was a special-case command sitting next to the rest of npm's command surface; install-scripts makes it part of the same family as install, install-test, and the rest. It also gives the team room to add subcommands: npm install-scripts ls is new in 11.18 and prints the current allow/deny list with package names and paths.
The release also adds PR #9662 (install-scripts: prune unused allowScripts entries), which sweeps the allow list every install and removes entries whose package is no longer in dependencies. This closes a long-standing wart where the allow list grew monotonically: if you approved esbuild for package-a, then removed esbuild from package-a's deps and added it to package-b's deps, the allow list kept the original entry. After 11.18 the sweep removes it as part of npm install, and the user is asked to re-approve under package-b.
The min-release-age audit-fix warning
PR #9564 (backport of #9544) teaches npm audit fix to detect when the fix it would apply is blocked by the minimumReleaseAge policy set in .npmrc. The same policy is shipped by pnpm, and the Astro 6.4.4 update flow surfaces it for @astrojs/upgrade. The npm version warns explicitly when the policy blocks an audit fix, instead of silently doing nothing:
npm audit fix
# 3 vulnerabilities (1 low, 1 moderate, 1 high)
#
# Could not auto-fix 1 vulnerability:
# package: glob-parent <6.0.0 (transitive via some-tool)
# blocked by minimumReleaseAge=4320 (cool-down window)
# set `minimum-release-age=0` or wait for the cool-down to expire.
The text is the same shape as the pnpm ERR_PNPM_MIN_RELEASE_AGE warning. The npm team's PR description says they "want users to be able to tell the difference between 'no fix available' and 'fix exists but is being held back by policy'," which the previous npm audit fix output did not.
SBOM and other fixes
Three npm sbom fixes ship in 11.18. PR #9693 percent-encodes the vcs_url qualifier in the generated purls, which closes a long-standing CP-tooling bug where a git+https://github.com/foo bar/bar URL (with a space) was emitted verbatim and rejected by downstream parsers. #9631 audits the non-isolated tree under the linked strategy so the SBOM and npm audit reports both pick up the full dep set. #9641 validates peerOptional conflicts in no-save mutations, which means npm install some-pkg --no-save will refuse to install if it would create a peerOptional conflict.
The release also fixes PR #9602 (don't flag inert optional deps in strict-allow-scripts), #9607 (approve deps with no resolved URL by name), and #9663 (close allowScripts enforcement gaps). Together they mean the strict-allow-scripts mode added in npm 11.5 now correctly distinguishes between packages that need scripts (the install hook is on, the audit hook will catch them) and packages that don't (the install hook is off because there is no install script).
Why this matters
--install-strategy=linked is npm's answer to the question pnpm has been answering since 2017: how do you stop a package from importing a transitive dependency it never declared? pnpm never carried the experimental flag; yarn berry ships PnP by default; npm is the third of the three to reach "stable and recommended for daily use." The graduation is the moment when the install model that everyone else treats as the default is finally a default-recommended path on npm.
For package authors the practical change is small and concrete. Add this to .npmrc:
install-strategy=linked
Then run npm install in CI as part of npm publish. If the build now fails because some test file imports an undeclared library, you have a real bug to fix before publishing, not a phantom dep that will explode in a coworker's clean install.
The same change applies to monorepo owners. The 11.18 fixes for npm exec (#9648), npm ls (#9664), workspaces (#9666), and the .store cleanup on strategy switch (#9649) close the gaps that made --install-strategy=linked unusable for workspaces before. A monorepo can ship install-strategy=linked in .npmrc at the root and rely on it propagating through workspaces.
The release is also the first one since the June 6 supply chain piece that closes the strict-allow-scripts audit gaps from that article. The --install-blocked-scripts and strict-allow-scripts flags from npm 11.5 are now usable end-to-end, and the new npm install-scripts namespace is where the approvals live.



