# Framework dependencies listed as peerDependencies

- **Pattern:** `ab-002360` (`sdk-package-quality.package-config.peer-deps`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002360
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002360)

## 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.

```json
{
  "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 `dependencies` and `peerDependencies` in `package.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 in `peerDependencies`, not `dependencies`. Common violations:
  - `react` or `react-dom` in `dependencies` instead of `peerDependencies`
  - `vue` in `dependencies` instead of `peerDependencies`
  - `@angular/core` in `dependencies` instead of `peerDependencies`
  - `next` in `dependencies` for a Next.js plugin
  - `express` in `dependencies` for Express middleware
- **Pass criteria:** Framework and runtime dependencies that consumers are expected to provide are listed in `peerDependencies` (with corresponding `peerDependenciesMeta` for optional ones). The package's `dependencies` only 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 `dependencies` instead of `peerDependencies`. 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_requires` with 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.

  ```json
  // 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 `devDependencies` for local development and testing. Use broad version ranges in `peerDependencies` to maximize compatibility.

## External references

- iso-25010 compatibility.interoperability
- ssdf PO.3.2
- slsa L1

Taxons: supply-chain, code-quality

HTML version: https://auditbuffet.com/patterns/ab-002360
