I have made a few simple reusable react components and wanted to know the correct way to include a dependency to react in my package.json for publishing with npm.
I am currently doing this:
Assuming my component will use the most recent version of react and I have tested and it works with that version. e.g. 0.13.3
"peerDependencies": {
"react": "^0.13.3"
},
For reusable components:
- Put a
react
dependency in both peerDependencies
and devDependencies
.
- Never put a
react
dependency in dependencies
.
peerDependencies
specifies which version(s) of React your reusable component supports/requires. When using npm 2 this also adds React to the list of modules to be installed, but this is no longer the case with npm 3.
devDependencies
ensures React will be installed when you run npm install
while developing your component, or when running tests on Travis or similar.
Putting react
in dependencies
will cause multiple versions of React to be installed if somebody uses your component but has a different version of React in their own package.json
- having multiple versions of React not only bloats the build, but also causes errors when different versions try to interact.
The selected answer is definitely the prescribed approach here however I've started favoring the use of inversion of control as opposed to relying on npm peer dependencies for my libraries dependencies and its served me well.
Libraries are easier if you build them functional. It seems to be easier to maintain libraries that export a single function which takes in an object with all of their heavy dependencies and export an object containing each of your libraries typical exports.
Library 'injected'
lib/index.js
export default ({ React }) => {
const InjectedComponent = props => (
<p style={{color: props.color}}>This component has no React npm dependencies.</p>
)
/** other stuff */
return { InjectedComponent }
}
Consuming App
app.js
import React from 'react'
import { render } from 'react-dom
/** Import the default export factory from our library */
import createInjectedComponent from 'injected'
/** Call the factory, passing its dependencies (guaranteed to match what we're bundling) and get back our component */
const { InjectedComponent } = createInjectedComponent({ React })
render(<InjectedComponent color="blue" />, document.getElementById('root'))
If your component only works with a given version of react or some other dependency, you can write some assertions around the version for the React parameter that is passed in. Overall, building libraries in this fashion should be less prone to new build issues appearing anytime version of React is published and will more importantly ensure that you are not causing your library consumers to bundle multiple versions of React and other heavy libraries. This pattern works well with npm link (I generally have 16+ libraries running from npm link simultaneous and experienced issues when I didn't use this pattern).
In your main app I would recommend always splitting out react, react dom, and any react lib components you use into a vendor bundle (webpack) and mark it as external in your main bundle so that you do not unintentionally bundle two versions.
You can have react
in either peerDependencies
or in dependencies
. The difference being that with a peerDependencies
, react
is only installed once for the package using your package. If you put it in dependencies
, react
will be installed twice, one time for the package using your package, and once time for your package.
React itself seems to favor peerDependencies
for some reason. You obviously don't want two separate versions of react
in your Javascript bundle (which happens by default if you use dependencies
), but that is easy to fix with npm dedupe
.
So there's no correct way to do it, both peerDependencies
and dependencies
work. Using dependencies
is more in line with the node/NPM way, but using peerDependencies
is friendlier to users of your package that doesn't know about npm dedupe
and why it's needed.