Framework dependencies listed as peerDependencies
Why it matters
Listing React, Vue, or any consumer-provided framework in dependencies instead of peerDependencies installs a second copy of that framework in the consumer's node_modules. For React specifically, this triggers the infamous "Invalid hook call" error because two separate React instances exist at runtime — a hard crash, not a degraded experience. It also silently doubles the framework's bundle weight. SSDF PS.3.2 flags this as a supply chain concern: you're injecting unverified versions of major frameworks into consumer apps. ISO 25010 interoperability fails when your version range conflicts with the host app.
Severity rationale
High because duplicate framework installations cause runtime crashes (React hooks, Vue reactivity) and inflated bundle sizes that directly affect the end user.
Remediation
Move framework dependencies from dependencies to peerDependencies with broad version ranges, and keep them in devDependencies for local development.
{
"peerDependencies": {
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"devDependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
}
For optional peer dependencies (e.g., a Vue adapter that's only needed for Vue users), add a peerDependenciesMeta entry: { "vue": { "optional": true } }. Test with the oldest and newest versions in your peer range.
Detection
-
ID:
peer-deps -
Severity:
high -
What to look for: Enumerate every dependency in package.json. For each, classify whether it should be a dependency, devDependency, or peerDependency. check
dependenciesandpeerDependenciesinpackage.json. If the package uses a framework or runtime that the consumer is expected to provide (React, Vue, Angular, Svelte, Node.js built-in modules), it should be inpeerDependencies, notdependencies. Common violations:reactorreact-domindependenciesinstead ofpeerDependenciesvueindependenciesinstead ofpeerDependencies@angular/coreindependenciesinstead ofpeerDependenciesnextindependenciesfor a Next.js pluginexpressindependenciesfor Express middleware
-
Pass criteria: Framework and runtime dependencies that consumers are expected to provide are listed in
peerDependencies(with correspondingpeerDependenciesMetafor optional ones). The package'sdependenciesonly contain libraries that the package itself bundles or needs to function independently — at least 1 framework dependency (React, Vue, etc.) must be a peerDependency if the SDK integrates with it. Report: "X dependencies classified, all Y correctly categorized." -
Fail criteria: A framework dependency that the consumer must provide (React, Vue, Angular, etc.) is listed in
dependenciesinstead ofpeerDependencies. This causes version duplication and potential runtime errors when the consumer has a different version installed. -
Skip (N/A) when: The package has no framework dependencies — it's a standalone utility with no peer runtime requirements. Also skip for Python (uses
install_requireswith version ranges), Rust (peer dependencies are not a convention), and Go (module system handles this differently). -
Detail on fail:
"react and react-dom are in dependencies instead of peerDependencies. This will install a separate copy of React alongside the consumer's version, causing 'Invalid hook call' errors and increased bundle size." -
Remediation: Peer dependencies tell npm that your package works WITH the consumer's version of a framework, rather than bringing its own copy.
// Before — bundling React with your library: { "dependencies": { "react": "^18.0.0", "react-dom": "^18.0.0" } } // After — declaring React as a peer: { "peerDependencies": { "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "devDependencies": { "react": "^18.0.0", "react-dom": "^18.0.0" } }Keep the framework in
devDependenciesfor local development and testing. Use broad version ranges inpeerDependenciesto maximize compatibility.
External references
- iso-25010:2011 · compatibility.interoperability — Interoperability — peer dep version alignment prevents duplicate runtime instances
- ssdf:800-218 · PO.3.2 — Review and update software dependency requirements
- slsa:1.0 · L1 — SLSA L1 — provenance of transitive dependencies
Taxons
History
- 2026-04-18·v1.0.0·Initial import from sdk-package-quality·automated