Markus Bergh

How to build reusable React components

How to build reusable React components

When building components that will be used in other applications, it can be a good idea to make them as simple as possible, with default properties that can be overriden, such as styles. Or even better, if the components are to be included in different systems the components could be grouped together in a package and installed as a dependency.

Building your own node packaged module is perfect for this purpose and with npm, it will be very easy to use it.

First some basics with npm but if you already know how to use it, skip to the React part directly.

Npm

Before installing and using packaged modules, you will need to install Node which includes the manager, preferably by heading over to https://nodejs.org/en/download/ and download the version that you need. The version I am using is the current LTS (Long Term Support) version of Node.

$ node --version
v6.11.2
$ npm --version
3.10.10

The npm project is, although bundled with Node, a separated project and tends to be updated more often. To update itself, you can run the following command ( -g installs it globally). You might need to use sudo when installing globally since the path is owned by root, and is usally in /usr/bin.

$ npm install npm@latest -g

You can change to where the packages are installed globally, if needed, by running npm config set prefix and specifying a path. If you're doing this, you will need to add it to your environment variable to be able to run global packages from the command line.

export PATH="/path/to/global/bin:$PATH"

Managing dependencies in your application

When installing packages to an application, you should add them to the package.json file, where the version is also added to match updates. You can specify whether a package is of a dependency, devDependency or peerDependency. For example using npm install foo --save-dev would install the package named "foo" as a local devDependency of the application.

A devDependency is usually a package used for development, such as running tests or builds. A peerDependency is of great deal when publishing your own package, where you would need the users dependency to be exact the same version when installing your package. For example react has a peerDependency of react-dom.

By not specifying a flag, the package would be installed as a dependency which is the default.

The biggest reason for using a package.json file is the portability of the application, where the user can clone and install everything necessary to run the whole thing and npm takes care of that task by using the package.json file as a reference.

To install a specific version, you run npm install foo@1.0.1 --save-dev. To uninstall packages, you run npm uninstall --save-dev with the flag to remove it from the package.json file.

Some handy npm commands (with aliases):

  • npm i foo (install package named foo)
  • npm i foo@1.0.1 (install version 1.0.1 of package named foo)
  • npm rm foo (uninstall package named foo)
  • npm outdated (list packages that can be updated)
  • npm up foo (update package named foo)
  • npm s foo (search for a package named foo)
  • npm ls (list all locally installed packages)

Building a library as a package

When building a library consisting of React components, I find it very easy to transpile and bundle it with webpack. I have an entry file which exports the components one by one, so that I can import only the one I am needing in the application.

// src/index.js

export Button from 'components/Button';
...
// src/components/Button.js

import React from 'react';
import PropTypes from 'prop-types';

const defaultStyles = {
  backgroundColor: green,
  border: '1px solid darkGreen'
};

const Button = ({
  children,
  styles
}) => {
  <button
    style={
      Object.assign({}, defaultStyles, styles)
    }
  >
    {children}
  </button>
};

Button.propTypes = {
  children: PropTypes.node.isRequired,
  styles: PropTypes.object
};

export default Button;

You will need to add some fields to the webpack configuration to have your code bundled as a library and there is a good section in the webpack documentation that covers this.

// config/webpack.config.babel.js

export default {
  ...
  output: {
    path: path.join(__dirname, '../lib'),
    filename: 'index.js',
    library: 'my-library',
    libraryTarget: 'commonjs2',
  },
  ...
}
// package.json

{
  "name": "my-library",
  "version": "1.0.0",
  ...
  "build": "npm run build:compile",
  "build:compile": "webpack --config=./config/webpack.config.babel.js",
  ...
}

When building — the files, after being transpiled, are bundled together and exported as a module with commonjs2 specification. You can read more about what options you have here.


When you are ready to publish your package, you make sure to bump your version (if updating it) in your package.json file and then run npm publish to publish it.

I can now install and import any existing component from my-library, and use it in my application.

$ npm install my-library --save
// layout.js

import React from 'react'

import { Button } from 'my-library';

const Layout = () => (
  <div>
    <Button>
      This is my button
    </Button>
  </div>
);

export default Layout;