Dependency Management Standard
This document defines the recommended practices for managing dependencies in JavaScript/TypeScript projects across Decentraland's ecosystem. It applies to front-end, back-end, SDKs, shared libraries, and any npm/yarn/pnpm-based package.
TL;DR:
- Version strategy → Use exact/fixed versions for
dependencies(security) anddevDependencies(consistent dev environment). Use version ranges only forpeerDependencies(flexibility).- Exception → Decentraland packages (
@dcl/*,decentraland-*) can use version ranges (^) as they are trusted internal packages.- Libraries/shared packages → Use
peerDependencieswith version ranges (^) for shared libraries (React, @dcl/schemas, etc.). Usedependencieswith exact versions only for utilities safe to duplicate.- Apps/final services → Use
dependencieswith exact versions for runtime packages (React, ethers, etc.).
1. Rationale
Many libraries rely on global singletons, React Context, class identity, shared caches, schema enums, or shared DB pools. Installing multiple versions of the same library can cause:
- Context not shared between copies
- Duplicate caches or pools
- Failing instanceof checks
- Enum/symbol mismatches
- Increased bundle size
- Hard-to-diagnose runtime bugs
Correct dependency management prevents these issues.
Non-goals
This standard does not attempt to:
- Enforce a single package manager (npm, yarn, pnpm are all supported)
- Guarantee a single physical copy in
node_modules(focus is on runtime/bundle deduplication)
2. Definitions
| Field | Purpose | Consumer responsibility | Applies to |
|---|---|---|---|
dependencies |
Packages used internally by the module | None - consumer is not expected to provide them | Apps and libraries |
peerDependencies |
Packages that must resolve to a single effective version at runtime | Must install them (or use peerDependenciesMeta for optional) | Libraries only |
peerDependenciesMeta |
Metadata for peerDependencies, used to mark peers as optional | None - only affects package manager behavior | Libraries only |
devDependencies |
Tooling only used during development | None | Apps and libraries |
About peerDependenciesMeta
peerDependenciesMeta is a field that provides metadata about your peerDependencies. The most common use case is marking peers as optional:
- Required peers (default): Package manager will warn if not installed
- Optional peers: Package manager won't warn if missing; your package should handle their absence gracefully
This is useful for packages that can work with or without certain dependencies (e.g., a utility that works with or without React, or a library that supports multiple Web3 providers).
Apps vs Libraries
Libraries / Shared Packages:
- Use
peerDependenciesfor packages that must resolve to a single effective version at runtime (React, ethers, @dcl/schemas) - Consumer (app) provides these dependencies
- Prevents duplicate installations and singleton conflicts
Apps / Final Services:
- Often use
dependenciesfor runtime packages (React, ethers, etc.) - They are the final consumer, so duplication is not a concern
peerDependenciesstill valid if the app might be consumed as a dependency
3. When to Use Each Field
Quick reference: See Section 4 for common packages and their recommended placement.
Use peerDependencies for (Libraries only):
In shared libraries/packages, use peerDependencies for packages that must resolve to a single effective version at runtime:
- React, Redux, wagmi, ethers, viem
- @dcl/schemas and similar cross-ecosystem libraries
- decentraland/connect
- DB drivers when sharing pools
- Any library relying on singletons or context
✅ Correct (Library):
{
"peerDependencies": {
"react": "^18.0.0",
"@dcl/schemas": "^20.0.0"
}
}
❌ Incorrect (Library using dependencies for shared libs):
{
"dependencies": {
"react": "^18.0.0"
}
}
Use dependencies for:
- Utilities safe to duplicate (lodash-es, date-fns)
- In apps: Runtime packages like React, ethers (when app is final consumer)
Important: Always use exact/fixed versions in
dependenciesfor security.
✅ Correct (Library):
{
"dependencies": {
"lodash-es": "4.17.21",
"date-fns": "3.6.0"
}
}
✅ Correct (App):
{
"dependencies": {
"react": "18.3.1",
"ethers": "6.13.0",
"lodash-es": "4.17.21"
}
}
Optional peerDependencies (peerDependenciesMeta)
For reusable packages that work with or without certain dependencies, use peerDependenciesMeta to mark peers as optional:
✅ Correct:
{
"peerDependencies": {
"react": "^18.0.0",
"ethers": "^6.0.0"
},
"peerDependenciesMeta": {
"ethers": {
"optional": true
}
}
}
What this means:
reactis required: Consumer must install it or package manager will warnethersis optional: Consumer doesn't need to install it; your package should check for its presence before using it
Use cases:
- Packages that work in both React and non-React environments
- Libraries that support multiple Web3 providers (ethers, viem, etc.)
- Utilities that enhance other libraries but aren't required
Use devDependencies for:
- Tooling (TypeScript, ESLint, testers, bundlers)
Important: Always use exact/fixed versions in
devDependenciesto ensure a consistent development environment across the team.
✅ Correct:
{
"devDependencies": {
"typescript": "5.4.5",
"eslint": "8.57.0",
"vitest": "1.6.0"
}
}
4. Examples
Packages that MUST be peerDependencies (shared context / singletons)
react,react-dom,react-redux,react-router-domredux,@reduxjs/toolkitethers,viem,wagmi@dcl/schemas,@dcl/ui-env,@dcl/cryptodecentraland-dapps,decentraland-ui,decentraland-ui2,decentraland-connectpg,pg-pool
Packages that SHOULD be dependencies (safe to duplicate)
lodash-es,date-fnsuuid,nanoidzod,ajvms,mitt,fp-future