Summary
@openzeppelin/adapter-evm (and, by symmetry, the other adapter-* packages) exposes framework-free per-capability subpath exports — e.g. @openzeppelin/adapter-evm/erc3643, /irs, /execution — which is exactly right for headless/backend consumers. However, the package's top-level dependencies still list the UI/React stack as hard runtime deps (@wagmi/core, @wagmi/connectors, @web3icons/react, lucide-react, and the React-facing UI packages). As a result, a headless consumer that imports only a capability subpath still pulls the entire UI/React dependency tree into node_modules at install time.
Impact
Headless / backend consumers (Relayer plugins, codegen-*, CLIs, indexers) that only need the framework-free capabilities get a heavily bloated install (wagmi + React + icon packages) they never import. It also muddies dependency audits and supply-chain review for backend services that should have no browser/React surface.
The imports themselves are clean — nothing UI is bundled into a headless consumer's build (the capability subpaths tree-shake correctly). This is purely about the install graph driven by the package-level dependencies manifest.
Concrete case
The OpenZeppelin Reference Implementations tokenized-deposits Relayer plugin (a headless backend) consumes only:
import { createERC3643 } from '@openzeppelin/adapter-evm/erc3643';
import { createIRS } from '@openzeppelin/adapter-evm/irs';
Yet pnpm why react / the install graph resolves the full wagmi + React + @web3icons/react + lucide-react set, because they are declared in adapter-evm's root dependencies.
Suggested direction (non-prescriptive)
Split the capability-only surface from the UI surface at the dependency level, so headless consumers get a lean install. Options, roughly in order of preference:
- Move the UI-only libraries (
@wagmi/*, @web3icons/react, lucide-react, React-facing UI packages) to optionalDependencies and/or peerDependencies (with peerDependenciesMeta.optional), so only consumers that import the UI subpaths install them.
- Or factor the UI subpaths into a separate package/entry, leaving the capability subpaths dependency-light.
The subpath exports already draw the framework-free boundary correctly; this is only about aligning the dependencies manifest with that boundary.
Environment
@openzeppelin/[email protected] (published, from npm)
- Consumer: headless Relayer plugin (Node ESM,
tsdown-built)
Summary
@openzeppelin/adapter-evm(and, by symmetry, the otheradapter-*packages) exposes framework-free per-capability subpath exports — e.g.@openzeppelin/adapter-evm/erc3643,/irs,/execution— which is exactly right for headless/backend consumers. However, the package's top-leveldependenciesstill list the UI/React stack as hard runtime deps (@wagmi/core,@wagmi/connectors,@web3icons/react,lucide-react, and the React-facing UI packages). As a result, a headless consumer that imports only a capability subpath still pulls the entire UI/React dependency tree intonode_modulesat install time.Impact
Headless / backend consumers (Relayer plugins,
codegen-*, CLIs, indexers) that only need the framework-free capabilities get a heavily bloated install (wagmi + React + icon packages) they never import. It also muddies dependency audits and supply-chain review for backend services that should have no browser/React surface.The imports themselves are clean — nothing UI is bundled into a headless consumer's build (the capability subpaths tree-shake correctly). This is purely about the install graph driven by the package-level
dependenciesmanifest.Concrete case
The OpenZeppelin Reference Implementations tokenized-deposits Relayer plugin (a headless backend) consumes only:
Yet
pnpm why react/ the install graph resolves the full wagmi + React +@web3icons/react+lucide-reactset, because they are declared inadapter-evm's rootdependencies.Suggested direction (non-prescriptive)
Split the capability-only surface from the UI surface at the dependency level, so headless consumers get a lean install. Options, roughly in order of preference:
@wagmi/*,@web3icons/react,lucide-react, React-facing UI packages) tooptionalDependenciesand/orpeerDependencies(withpeerDependenciesMeta.optional), so only consumers that import the UI subpaths install them.The subpath exports already draw the framework-free boundary correctly; this is only about aligning the
dependenciesmanifest with that boundary.Environment
@openzeppelin/[email protected](published, from npm)tsdown-built)