Safeguard
Developer Tools•

Can npm install-strategy linked catch phantom dependencies?

npm's linked install strategy exposes only declared dependencies, so an undeclared import fails in development. What it catches, misses, and how to use it.

Safeguard Security Team
3 min read

Yes, partly. With install-strategy set to linked, npm installs packages into a store and links only each package's declared dependencies, so an import of something you never listed fails during development. The npm docs recommend it for package authors, and also say it does not catch every case.

What a phantom dependency is

Under npm's default hoisted layout, packages are placed as high in node_modules as they can go. A transitive dependency of something you installed may land at the top, where your own code can import it even though it is not in your package.json. Your build passes. A user who installs your package alone does not get that transitive dependency, and your code fails for them.

It is a packaging bug with a security side. You depend on code whose version you never chose, and a change in someone else's tree can change what you import.

The four strategies

The npm config documents these values for install-strategy:

ValueWhat it does
hoisted (default)Installs non-duplicated packages at the top level, duplicated ones deeper as needed
nestedInstalls in place with no hoisting (formerly legacy-bundling)
shallowInstalls only direct dependencies at the top level (formerly global-style)
linkedInstalls into node_modules/.store and links in place, unhoisted

Using linked to check your package

The npm developer guide suggests this while developing a package you plan to publish:

npm install --install-strategy=linked
npm test

The isolated layout only exposes a package's declared dependencies. An import of an undeclared package then fails for you in development, instead of resolving by accident.

What it does not catch

The docs include a note you should take seriously. A dependency that is still satisfied by a devDependency, or by the workspace root's node_modules, can resolve fine for you and still be missing for whoever installs your package. So linked mode is one check, not a guarantee.

To fill the gap:

  1. Run npm pack --dry-run and inspect what ships.
  2. Install the packed tarball into an empty folder and run your entry point.
  3. Compare imports in your source against the dependencies field, not devDependencies, for runtime code.
  4. Add the install and test step to CI so it is repeated.

Our post on phantom dependencies and how pnpm blocks them covers the same idea with pnpm's default layout, and npm-explain helps trace why something is present.

Trade-offs

Linked mode changes the node_modules layout, and some tools expect a flat tree. Try it in a branch before making it team policy. If a tool breaks, that is often the tool relying on an undeclared dependency, which is worth knowing.

Where Safeguard fits

Safeguard's dependency scanning and SBOM generation work from your lockfiles, so declared and resolved components both show up in the inventory, and reachability analysis helps separate vulnerable code you call from code you do not. A free scan is at https://gold.safeguard.sh/scan.

Quick check

What is the default npm install strategy?

hoisted. Linked is an opt in value that installs into node_modules/.store and links packages in place.

If linked mode passes, can I publish safely?

Not on that evidence alone. The npm docs say a dependency satisfied by a devDependency or the workspace root can still be missing for your users, so also test the packed tarball in a clean folder.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.