React and react-router are widely used and well-maintained. The real supply-chain exposure
in a React app isn't React — it's the sprawling transitive tree (build tools, polyfills, small utilities)
that a fresh create-react-app or Vite template pulls in.
See what you actually depend on
npm ls --all | wc -l # how deep the tree really is
npm ls react react-dom react-router # confirm expected versions/dedupe
Lock it down
- Commit
package-lock.jsonand review its diffs in CI — that's where a swapped transitive dependency shows up. - Pin direct deps to exact versions; let the lockfile fix the rest.
- Run
npm install --ignore-scriptsin CI and audit any package that genuinely needs a lifecycle script. - Scan the lockfile:
osv-scanner --lockfile package-lock.json.
Watch the small utilities
Historically the painful incidents have been tiny, ubiquitous helpers (left-pad-scale packages), not the framework. A one-maintainer utility deep in your tree with a sudden release is worth more scrutiny than React's next minor.