A guide + boilerplate for publishing modern React modules with Rollup.
Intro
Publishing an open source React / Preact component or library to npm can be very rewarding, but getting started with the setup is still more daunting than it should be, especially compared to the relatively easy process of creating a React app via create-react-app or create-preact-app.
Because JS module formats and ES features are all over the place and are unlikely to be normalized anytime soon, npm modules must support both the lowest common denominator format (commonjs) as well as modern ES6 modules without relying on commonplace language features like JSX and class properties that most of the React community takes for granted. For this reason, it’s pretty difficult to take a component you’ve written for an app and publish it directly to npm.
The purpose of this article is to help React authors easily publish their own, high quality component modules.
Goals
There are some existing React library boilerplates, but none of them fulfilled the following goals which we set out to accomplish:
- Support all possible JS language features during development
- Build process to convert source to commonjs and es module formats required for practical usage on npm
- Use Rollup for build process and Babel for transpilation (we discuss Rollup vs Webpack later on in this article)
- Must come with an example app using the create-react-app standard
- Allow the use of npm modules within your library, either as dependencies or peer-dependencies
- Support importing CSS in your module (note that CSS support will be a noop if you’re using a css-in-js approach)
- Thorough documentation
It’s particularly important for module authors to include a simple, self-contained example app alongside the module itself, as it serves two useful purposes:
- As a local, hot-reload server for developing your module
- Easily publishable to github pages so users can quickly demo your module (or comparable hosting alternatives like surge.sh or now.sh)
Now that our goals are clearly defined, check out the boilerplate repo we’ll be starting from:react-modern-library-boilerplate - Boilerplate for publishing modern React modules with Rollup
Walkthrough
In order to use the boilerplate, we recommend following this walkthrough to start out. Don’t worry if these steps seems complicated, as it’s meant to be very verbose.
Getting Started
Let’s create an example npm module called
react-poop-emoji
that exposes a single component, PoopEmoji
💩. We'll assume an example github username of github-haxor
, where your github username will be used for specifying the repository in package.json
and resolving the example github pages deployment.# clone and rename base boilerplate repo git clone https://github.com/transitive-bullshit/react-modern-library-boilerplate.git mv react-modern-library-boilerplate react-poop-emoji cd react-poop-emoji rm -rf .git # replace boilerplate placeholders with your module-specific values mv README.template.md README.md # find and replace react-modern-library-boilerplate with react-poop-emoji everywhere
Local Development
Now you’re ready to run a local version of rollup that will watch your
src/
component and automatically recompile it into dist/
whenever you make changes.We’ll also be running our
example/
create-react-app that's linked to the local version of your react-poop-emoji
module.# run example to start developing your new component against npm link # the link commands are important for local development npm install # disregard any warnings about missing peer dependencies npm start # runs rollup with watch flag # (in another tab, run the example create-react-app) cd example npm link react-poop-emoji npm install npm start # runs create-react-app hot-reload dev serve
Now, anytime you make a change to your component in src/ or to the example application's example/src, create-react-app will live-reload your local dev server so you can iterate on your component in real-time.
import React, { Component } from 'react' import PropTypes from 'prop-types' // example of built-in support for importing css styles (optional) import './styles.css' export default class ExampleComponent extends Component { static propTypes = { text: PropTypes.string } render() { const { text } = this.props return ( <div> Example Component: {text} </div> ) } }
Here we have the default exported component that comes with the boilerplate. Feel free to edit it to your liking before moving on while testing your changes in the live create-react-app dev server.
Git Stuffs
When you’re ready to push your component for the first time to github, make sure you’ve customized all your readme and metadata, then initialize your git repo normally:
# be sure to update docs vim README.md vim package.json # init and push git repo git init git add * git commit -am "init" # add git remote and push to remote github repo
NPM Stuffs
When you’re ready to publish your module to npm, make sure your dependencies are up-to-date. Any npm module dependencies that you don’t want to be included in the published bundle should be marked as peer dependencies in
package.json
and added to the externals array in your rollup config. Note that this boilerplate defaults to setting react
, react-dom
, and prop-types
as peer dependencies, which is probably what you want unless you really know what you’re doing.# update dependencies, devDependencies, and peerDependencies vim package.json vim rollup.config.js # build dist and publish to npm npm publish
Github Pages
And finally, we recommend deploying your example to github pages so your users can quickly play around with a live version of your library before installing it.
Deploying to github pages is straightforward. We create a production build of our example
create-react-app
that showcases your library and then run gh-pages
to deploy the resulting bundle. This can be done with the command:npm run deploy
Note that it’s important for your
example/package.json
to have the correct homepage
property set, as create-react-app
uses this value as a prefix for resolving static asset URLs.We recommend adding a link to the resulting github pages example to your readme.
FAQ
Why use Rollup over Webpack?
For a deeper explanation, I recommend reading Rich Harris’ article Webpack and Rollup: the same but different. In short, the majority of the community now favors using Rollup for libraries and Webpack for apps. That being said, I believe you should stick with whatever you’re more comfortable with, as there really isn’t that big of a difference between the two as long as you’re comfortable using one versus the other.
import babel from 'rollup-plugin-babel' import commonjs from 'rollup-plugin-commonjs' import postcss from 'rollup-plugin-postcss' import resolve from 'rollup-plugin-node-resolve' import pkg from './package.json' export default { input: 'src/index.js', output: [ { file: pkg.main, format: 'cjs' }, { file: pkg.module, format: 'es' } ], external: [ 'react', 'react-dom', 'prop-types' ], plugins: [ resolve(), commonjs(), postcss({}), babel({ exclude: 'node_modules/**' }) ] }
Rollup configs are generally more concise for library development. This config transpiles and bundles the module’s source and external dependencies to CommonJS and ES6 formats.
Why use CRA for the example?
create-react-app has become a standard that nearly every react developer is familiar with. Its internal design choices and tradeoffs represent a great deal of collaboration among many of the best developers in the React community.
We feel that by taking advantage of such a standard application framework, module authors can provide the simplest possible example app that both acts as a mature, local development vehicle while iterating on your module as well as being easily publishable as an example showcase.
See this CRA issue for more context around this movement.
Where are the tests?
I recommend that you piggyback off of create-react-app’s built-in test harness setup for testing your library. That being said, feel free to submit a PR and I’d be happy to add some separate, standardized testing to the boilerplate.
What’s the purpose of creating boilerplate? Won’t it be outdated in a month?
This is legitimately a great question. I’d like to thank myself for asking it 😝
Even though the JS community typically moves too fast for its own good, I believe these types of point-in-time best practice boilerplates still serve a useful learning purpose and jumping off point for both aspiring open source authors and veterans alike. I was personally frustrated that it was so difficult to find a quality, up-to-date starting point after publishing several open source react modules, so I wanted to take what I had learned and give back to the community that has taught me so much.
If you have any suggestions on how to improve this boilerplate or walkthrough, or if something’s out-of-date a month from now, feel free to raise an issue or, even better, submit a PR!
How can I use use Typescript in my components?
Rollup has excellent Typescript support. For details on how to integrate Typescript with this boilerplate, see this issue.
What does a published module look like?
Here is an example react module created from this guide: react-background-slideshow, a sexy tiled background slideshow for React. It comes with an example create-react-app hosted on github pages and should give you a good idea of the type of module you’ll be able to create starting from this boilerplate.
Wrapping up
I’d like to throw a shout out to js.coach, which is my favorite resource for finding high quality, open source React components. If you’re looking for further inspiration or are trying to find a solid solution without reinventing the wheel, chances are there will be some relevant modules listed on their index.
If you find this walkthrough or accompanying boilerplate useful, please ⭐️ ️️ the repo to help other developers find it.
Disclaimer: the author is a software engineer at Facebook, but this article discusses an independent open source project, and my views do not reflect the views of Facebook or its engineering staff as a whole. E.g., I write about this type of stuff for fun. 😃
Follow me on twitter for more awesome stuff like this @transitive_bs