/*** examples/src/index.js ***/ import React from 'react';

import { render} from 'react-dom';

import MyComponent from '../../src'; const App = () => (

<MyComponent />

); render(<App />, document.getElementById("root"));

Note that the demo imports MyComponent from ../../src .

Let’s set up Webpack. Add webpack.config.js to the project root:

/*** webpack.config.js ***/ const path = require('path');

const HtmlWebpackPlugin = require("html-webpack-plugin");

const htmlWebpackPlugin = new HtmlWebpackPlugin({

template: path.join(__dirname, "examples/src/index.html"),

filename: "./index.html"

});

module.exports = {

entry: path.join(__dirname, "examples/src/index.js"),

module: {

rules: [

{

test: /\.(js|jsx)$/,

use: "babel-loader",

exclude: /node_modules/

},

{

test: /\.css$/,

use: ["style-loader", "css-loader"]

}

]

},

plugins: [htmlWebpackPlugin],

resolve: {

extensions: [".js", ".jsx"]

},

devServer: {

port: 3001

}

};

This configures Webpack to do the following:

Resolve source dependencies using examples/src/index.js as a starting point

as a starting point Use Babel to transpile .js and .jsx files via babel-loader

Resolve CSS dependencies and inject inline styles via css-loader and style-loader

and Automatically inject a script reference to the bundle output in examples/src/index.html via html-webpack-plugin

via Serve the demo on port 3001

Finally, we need to tell Babel which transpilations we want. We definitely want to transpile JSX syntax into React core API calls. Let’s say we also want to transpile javascript down to ES5 to reach more browsers. This is a common configuration which is easily applied via a couple of presets. Add a .babelrc file to the project root like so:

{

"presets": ["env", "react"]

}

Let’s run the demo:

npm start

Point your browser to localhost:3001 and you should see the demo page for your component. Make some changes and hit save, and the page will automatically refresh. Our dev environment is working in watch mode!

Let’s proceed with the setup needed to support the second workflow.

Publish a transpiled, ready-to-use version of your component to npm

Publishing to npm is an easy, automated process, but there are a couple of setup steps to take first.

We want to publish a Babel-transpiled version of the minimum files needed to install and use the component. Pre-transpilation prepares the component for use even in target projects that are not using Babel, e.g., those not using JSX syntax.

First, let’s install the Babel CLI:

npm i babel-cli -D

Now add a transpile script which we will use to cause Babel to transpile our component source files, as well as copy any non-source assets (e.g., .css files), into a target folder named dist .

At the same time, let’s set the main entry point of our component to the transpiled version.

Make the following modifications to package.json :

{

"name": "my-component",

"version": "1.0.0",

"description": "",

"main": "dist/index.js",

"scripts": {

"test": "echo \"Error: no test specified\" && exit 1",

"start": "webpack-dev-server --mode development",

"transpile": "babel src -d dist --copy-files"

},

"author": "",

"license": "ISC",

"devDependencies": {

"babel-cli": "^6.26.0",

"babel-core": "^6.26.0",

"babel-loader": "^7.1.4",

"babel-preset-env": "^1.6.1",

"babel-preset-react": "^6.24.1",

"css-loader": "^0.28.11",

"html-webpack-plugin": "^3.2.0",

"react": "^16.3.2",

"react-dom": "^16.3.2",

"style-loader": "^0.20.3",

"webpack": "^4.5.0",

"webpack-cli": "^2.0.14",

"webpack-dev-server": "^3.1.3"

}

}

Try it out:

npm run transpile

There should now be a dist folder in the project root, containing a transpiled version of index.js , along with a copy of styles.css . These are the ready-to-use files that a user can import and use in their project.

Let’s simplify our workflow by adding a prepublishOnly script. This script will be run automatically by npm every time we publish our component. This ensures that we always publish the latest transpiled version of our component.

At the same time, let’s specify that we expect target projects using our component to have a specific version of React installed. Expressing this as a peerDependency causes npm to not include React in the published package, which reduces package size and avoids the disaster of having multiple versions of React in the user’s target project.

Make the following modifications to package.json:

{

"name": "my-component",

"version": "1.0.0",

"description": "",

"main": "dist/index.js",

"scripts": {

"test": "echo \"Error: no test specified\" && exit 1",

"start": "webpack-dev-server --mode development",

"transpile": "babel src -d dist",

"prepublishOnly": "npm run transpile"

},

"author": "",

"license": "ISC",

"peerDependencies": {

"react": "^16.3.0",

"react-dom": "^16.3.0"

},

"devDependencies": {

"babel-cli": "^6.26.0",

"babel-core": "^6.26.0",

"babel-loader": "^7.1.4",

"babel-preset-env": "^1.6.1",

"babel-preset-react": "^6.24.1",

"css-loader": "^0.28.11",

"html-webpack-plugin": "^3.2.0",

"react": "^16.3.1",

"react-dom": "^16.3.1",

"style-loader": "^0.20.3",

"webpack": "^4.5.0",

"webpack-cli": "^2.0.14",

"webpack-dev-server": "^3.1.3"

}

}

Finally, let’s inform npm that there are files and folders in our project that can be omitted from the published package. Add .npmignore to the project root.

# .npmignore src

examples

.babelrc

.gitignore

webpack.config.js

We are now ready to publish our component to npm:

npm publish

When you browse your profile on npm, you should see the new package. Your component is shipped!

Let’s set up the last workflow.

Publish an online demo to GitHub Pages

Hosting our online demo on GitHub Pages is free. It requires using Webpack to build a Production version, and then pushing it to a special branch in our GitHub repo. Let’s automate this.

First, install a helper package which will maintain the special GitHub branch for us. Don’t worry that we haven’t added Git source control to our project yet; we will get to that in a moment.

npm i gh-pages -D

Now let’s add three new scripts to package.json :

{

"name": "my-component",

"version": "1.0.0",

"description": "",

"main": "dist/index.js",

"scripts": {

"test": "echo \"Error: no test specified\" && exit 1",

"start": "webpack-dev-server --mode development",

"transpile": "babel src -d dist --copy-files",

"prepublishOnly": "npm run transpile",

"build": "webpack --mode production",

"deploy": "gh-pages -d examples/dist",

"publish-demo": "npm run build && npm run deploy"

},

"author": "",

"license": "ISC",

"peerDependencies": {

"react": "^16.3.0",

"react-dom": "^16.3.0"

},

"devDependencies": {

"babel-cli": "^6.26.0",

"babel-core": "^6.26.0",

"babel-loader": "^7.1.4",

"babel-preset-env": "^1.6.1",

"babel-preset-react": "^6.24.1",

"css-loader": "^0.28.11",

"gh-pages": "^1.1.0",

"html-webpack-plugin": "^3.2.0",

"react": "^16.3.2",

"react-dom": "^16.3.2",

"style-loader": "^0.20.3",

"webpack": "^4.5.0",

"webpack-cli": "^2.0.14",

"webpack-dev-server": "^3.1.3"

}

}

The build script causes Webpack to build a bundled, minified Production version of our demo, but we need to tell Webpack where to put the resulting files:

/*** webpack.config.js ***/ const path = require('path');

const HtmlWebpackPlugin = require("html-webpack-plugin");

const htmlWebpackPlugin = new HtmlWebpackPlugin({

template: path.join(__dirname, "examples/src/index.html"),

filename: "./index.html"

});

module.exports = {

entry: path.join(__dirname, "examples/src/index.js"),

output: {

path: path.join(__dirname, "examples/dist"),

filename: "bundle.js"

},

module: {

rules: [

{

test: /\.(js|jsx)$/,

use: "babel-loader",

exclude: /node_modules/

},

{

test: /\.css$/,

use: ["style-loader", "css-loader"]

}

]

},

plugins: [htmlWebpackPlugin],

resolve: {

extensions: [".js", ".jsx"]

},

devServer: {

port: 3001

}

};

Let’s give it a try:

npm run build

You will find the Production build in examples/dist .

Now let’s add Git source control to our project. Start by adding a .gitignore file in the project root, which we use to exclude non-source files from source control:

# .gitignore node_modules

dist

Go to GitHub and create a new repository for your project. From the screen that subsequently appears, copy and run the commands under the heading ...or create a new respository on the command line , which will initialize a local Git repo and connect it to the remote GitHub repo.

Now that our local and remote repos are created and linked, we are ready to set up the demo hosting environment. This requires configuring our GitHub repo to look for our demo build in a branch named gh-pages. Our deploy script uses the gh-pages package to perform this setup for us, and to maintain the branch contents. Let’s test it out:

npm run deploy

Click the Settings link in your repository in GitHub and scroll down to the GitHub Pages section. You should see a link to your demo. Your demo is online!

Finally, the publish-demo script we added simplifies our workflow even further by chaining the build and deploy scripts together into a single script:

npm run publish-demo

Wrapping up

Whenever you are ready to publish a new version, simply increment the version in package.json , and then run npm publish and npm run publish-demo . Publishing a new version to npm is effective immediately, while publishing a new demo version to GitHub Pages can take 20 minutes or so to become effective.

There are other things you will want to do to complete your component’s community presence, which are beyond the scope of this article. Here are a few to get you going:

Develop your project’s README.md: add a description of the component, a link to the demo, example usage, and specify the component’s API

Add automated tests

Populate the description and repository fields in package.json

Consider what license you will offer with your component

Thanks for reading!