Creating and Publishing in NPM a React Component in 2024

Jose Ciccio
8 min readApr 16, 2024

--

Photo by Lautaro Andreani on Unsplash

I’ve been wanting to do this post for a while. For some reason I’ve found some guides that, while are really good, are outdated from what I need or end up giving me some errors since I already have some published components.

I will try to make it as condensated and small as possible so you don’t fall asleep since probably you just want to know how to build run and upload your component into NPM to share it to the world :)

In order to explain this process I’ll use a component of my own named: react-js-banner. It is a simple line text banner component (warning, error, success, etc).

react-js-banner Component

It’s going to be a subset of that component since there are some things that may not be relevant. But something that I want to add in this guide is how to build and add the ability to load css.

Some prerequisites

Before creating and publishing your NPM component, ensure you have the following prerequisites installed:

  1. Node.js and npm: Make sure you have Node.js installed on your system, which includes npm, the Node package manager. This will enable us to upload the component into NPM => Download Node
  2. (Optional) I like to use Yarn, so you can go ahead and download it as well if you want, since I feel is less error prompt. => Download Yarn
  3. A code editor: Use any code editor of your choice. Popular options include Visual Studio Code (VSCodium the open source one), Sublime Text, or Atom.
  4. Git: Install Git for version control.

Project Structure

For the sake of this entry, the way that the project will be structured is going to be a component which we will be able to build and a second component that will be used for testing. We will be able to create a build and link the new component as other people over the internet will do :)

Project Structure

Setting Up Your Component Project

Start by creating a new directory for your project and navigate into it via the terminal (this should work on both Mac and PC).

mkdir react-js-banner
cd react-js-banner

Initialize your project with npm.

npm init -y

This command creates a package.json file with default values.

Let’s go ahead and edit the package.json in order to add what we need, I’m using the dev dependencies to add some packages like babel, react, react-dom, etc. Also css-loader to be able to add new css styles. In order to create the build and bundle all the assets I’m using webpack.

{
"name": "react-js-banner",
"version": "0.7.2",
"description": "React JS Banner component customizable with auto hide option",
"main": "build/index.js",
"peerDependencies": {},
"dependencies": {
},
"scripts": {
"start": "webpack - watch",
"build": "webpack"
},
"devDependencies": {
"@babel/core": "7.13.14",
"@babel/plugin-proposal-class-properties": "7.13.0",
"@babel/preset-env": "7.13.12",
"@babel/preset-react": "7.13.13",
"babel-loader": "8.2.2",
"css-loader": "5.2.0",
"eslint": "6.8.0",
"prop-types": "15.8.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"style-loader": "3.3.4",
"webpack": "5.91.0",
"webpack-cli": "5.1.4"
},
"repository": {
"type": "git",
"url": "https://github.com/jciccio/react-js-banner.git"
},
"keywords": [
"react js banner",
"js banner",
"banner",
"message notification",
"notification",
"toaster"
],
"license": "MIT",
"homepage": "https://github.com/jciccio/react-js-banner/blob/master/README.md"
}

We need two more config files, the babel one and a webpack.

Let’s start with one on the root folder called .babelrc

Babel is a toolchain that is mainly used to convert ECMAScript 2015+ code into a backwards compatible version of JavaScript in current and older browsers or environments.

{
"presets":[
"@babel/preset-env",
[
"@babel/preset-react",
{
"runtime": "automatic"
}
]
]
}

Last the webpack.config.js (Webpack helps you bundle all your project assets, dependencies, etc for a clean build).

var path = require("path");

module.exports = {
mode: "production",
entry: "./src/index.js",
output: {
path: path.resolve("build"),
filename: "index.js",
libraryTarget: "commonjs2"
},
module: {
rules: [
{ test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" },
{
test: /\.css$/,
loader: "style-loader"
},
{
test: /\.css$/,
loader: "css-loader"
}
]
},
externals: {
react: "react"
}
};

We want to bundle the js files, css files and use react as dependency. We will resolve everything and put it into a build folder and something important is that we want to exclude the node_modules folder.

Something else, I’m assuming that the entry is called index.js but you can change it for the path you want in case that the file will be named in a different way.

On the mode, I suggest you to change it from: mode: “production”, to “development” while you do your internal tests and back to prod when you decide to publish it to npm.

Writing Your React Component

import React from 'react';

const MyComponent = () => {
return (
<div>
<h1>Hello, World!</h1>
</div>
);
};

export default MyComponent;

In order to build your new component execute:

yarn run build

If everything works ok you should get a compiled successfully:

webpack 5.91.0 compiled successfully in 5525 ms
Done in 7.47s.

So for my component, this is just to have it, but you can run everything with the hello world example above and should work.

This code I’m sharing it is for you understand why I used css loaders and Babel later I have something like this:

import React, { Component } from "react";
import PropTypes from 'prop-types';
import "./banner.css";

/**
* Banner component
* @author [Jose Antonio Ciccio](https://github.com/jciccio)
*/
class Banner extends Component {
constructor(props) {
super(props);
this.state = {
showBanner: undefined
};
}

componentDidMount() {
this.hideBanner();
}

render() {
let appearTime = this.props.transitionAppearTime ? this.props.transitionAppearTime: 1000;
let transitionTime = this.props.transitionTime ? this.props.transitionTime: 1000;
return (
this.renderBanner()
);
}

renderImage(){
if (this.props.image && this.props.imageClass){
return(
<img
src={this.props.image}
className={this.props.imageClass}
/>
)
}
else if(this.props.image){
return (
<img
src={this.props.image}
/>
)
}
else{
return null;
}
}

async hideBanner() {
if(this.props.visibleTime !== undefined && this.props.visibleTime > 0)
{
this.timeout(this.props.visibleTime);
}
}

renderBanner() {
const showBanner = this.state.showBanner !== undefined ? this.state.showBanner : true;
const visibleTimeAnim = (this.props.visibleTime > 0) ? `opacityOn ${this.props.visibleTime/1000}s` : `noFadeOut 3s`;
const animation = {"animation": `${visibleTimeAnim} normal forwards`}
if(showBanner){
if (this.props.title && (this.state.show === undefined || this.state.showBanner) )
return (
<div key="banner" className="banner" style={{...this.props.css, ...animation}}>
{this.renderImage()}
{this.renderTitle()}
</div>
);
}
else if (this.props.children){
return(
<div key="banner" className="banner" style={{...this.props.css, ...animation}}>
{this.props.children}
</div>
)
}
else {
return null;
}
}
return null;
}

renderTitle(){
return <div key={`BannerId-${this.props.id}`}>{this.props.title}</div>
}

timeout(ms) {
setTimeout(() => {
if(this.props.onHideCallback != null){
this.props.onHideCallback(this.props.id);
}
}, ms);
}

}

Banner.propTypes = {
title: PropTypes.node,
css: PropTypes.object,
visibleTime: PropTypes.number,
... more props
onHideCallback: PropTypes.func
};

Banner.defaultProps = {
onHideCallback: null,
... Some default props
}

export default Banner;

I’ll update this article later, I’ll transform this class into a functional component later :)

After that we need to link our component so it can be used elsewhere, that can be done by executing:

yarn link

Now let’s create another project that will serve as an example where we’re going to import the component we just made, so we will do the same process:

First create the folder where we will store the example (I usually do it inside the same project so you will upload it and will be accessible easily for everyone)

mkdir react-js-banner-example
cd react-js-banner-example

Now we continue by initializing the project:

npm/yarn init -y

npm init -y

And now for the magic, in order to use our component, we need to link the project we just created so it can be used inside the example:

npm/yarn link <name-of-your-component>

npm link react-js-banner

From here, it is just a matter of creating your package.json and your example component that will use the one we’re building. I used react-scripts for this one.

My package.json looks like this one. Note that in here it is a private one.

This way you’ll be able to test as anyone would.

{
"name": "react-js-banner-example",
"version": "0.2.0",
"private": true,
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "^5.0.1"
},
"scripts": {
"start": "react-scripts --openssl-legacy-provider start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"yarn-audit-fix": "^10.0.7"
}
}

Next, the example in the App.js:

import React, { Component } from 'react';
import Banner from 'react-js-banner';
import './App.css';
import logo from './logo.svg';

class App extends Component {

constructor(props) {
super(props);
this.state = {
banner1Css: { color: "#FFF", backgroundColor: "green" },
banner2Css: { color: "#000", backgroundColor: "grey", fontFamily: "arial" },
banner3Css: { color: "#FFF", backgroundColor: "red", fontSize: 20 }
};
}

render() {
return (
<div className="App">
<header className="App-header">
<h1 className="App-title">react-js-banner Component</h1>
</header>
<p>
Basic Banner Usage
</p>
<Banner id="banner1" title="This is an example banner" showBanner={true} />
<Banner id="banner2" title="This is an example banner with CSS" css={this.state.banner1Css}/>
<Banner id="banner3" title="This is an example banner with Another CSS" css={this.state.banner2Css}/>
<Banner
id="banner4"
title="This is an example banner with CSS and 3 Seconds of Visibility"
css={this.state.banner3Css}
visibleTime={3000}
onHideCallback={(bannerId) => alert('This is an example banner with CSS and 3 Seconds of Visibility Hidden')}
/>
<Banner
id="banner5"
title="This is an example banner with CSS and Image"
image={logo}
imageClass="App-logo"
css={this.state.banner2Css}
/>

<Banner
id="banner6"
title={[<div key="banner1">Title prop supports HTML tags <i>Italics</i><b> Bold</b> etc.</div>]}
/>

<Banner id="banner7" showBanner={true} >
<div>
<h1>Or you can simply pass children with whatever you need</h1>
<h2>h2</h2>
<h3>h3</h3>
</div>
</Banner>
</div>
);
}
}

export default App;

Finally you can just do yarn start and should see what the result is.

Publish it!

This part is a really simple one! :)

Create an npm account https://www.npmjs.com/

Navigate to your folder an run:

npm publish

And… That’s it! Now you have a public npm component for the entire world Cheers!

In case that you want to check my components you can find them here:

My Github repo:

React JS Search

React JS Banner

React JS Paginator

React JS Table with CSV download

File uploader:

Hope you find this guide useful!

--

--

Jose Ciccio

Engineer Manager, Software Developer, University teacher and Indie Game Developer.