How to package your React Component for distribution via NPM (2024)

I wrote a React component, transpiling using Babel, bundling and building using Webpack. I wanted to use it in another application via NPM. My NPM publish package needed to include component behavior, styles and images. So how difficult is it to package my React Component for distribution via NPM? An hour or so of work maybe right?

Well, it took me a lot longer to figure this out because of the following speed-bumps.
– React version conflict
– How to bundle and consume styles from a component
– How to include and bundle images

Here are the steps.

npm init

In the package.json, make sure these fields are populated:

package.json{
"name": "myUnflappableComponent",
"version": "0.0.29",
"main": "dist/index.js",
"publishConfig": {
"access": "restricted"
},
...
}

In package.json, add React and react-dom in the project’s peerDependencies (And remove it from dependencies, but add it to devDependencies for development)

..."peerDependencies": { 
"react": ">=15.0.1",
"react-dom": ">=15.0.1"
},
"devDependencies": {
"react": ">=15.0.1",
"react-dom": ">=15.0.1"
},
...

In your webpack configuration, create a UMD bundle

...
module.exports = {
...
output: {
path: path.join(__dirname, './dist'),
filename: 'myUnflappableComponent.js',
library: libraryName,
libraryTarget: 'umd',
publicPath: '/dist/',
umdNamedDefine: true
},
plugins: {...},
module: {...},
resolve: {...},
externals: {...}
}

And super-duper important, don’t bundle React

module.exports = { 
output: {...},
plugins: {...},
module: {...},
resolve: {
alias: {
'react': path.resolve(__dirname, './node_modules/react'),
'react-dom': path.resolve(__dirname, './node_modules/react-dom'),
}
},
externals: {
// Don't bundle react or react-dom
react: {
commonjs: "react",
commonjs2: "react",
amd: "React",
root: "React"
},
"react-dom": {
commonjs: "react-dom",
commonjs2: "react-dom",
amd: "ReactDOM",
root: "ReactDOM"
}
}
}

If you don’t set up a .npmignore file, npm uses your .gitignore file and bad things will happen. An empty .npmignore file is allowed. This is what mine looks like:

webpack.local.config.js
webpack.production.config.js
.eslintrc
.gitignore

To build before publishing.

"scripts": {
"prepublish": "rm -rf ./dist && npm run build",
...
}

We use SCSS files for our styles. These are compiled into css and extracted out by Webpack.

Install the following:

npm install --save-dev extract-text-webpack-plugin node-sass style-loader css-loader sass-loader

Update your webpack.config

const ExtractTextPlugin = require('extract-text-webpack-plugin');module.exports = {
...
plugins:[
new ExtractTextPlugin({
filename: 'myUnflappableComponent.css',
}),
],
module:{
rules:[
{
test: /\.*css$/,
use : ExtractTextPlugin.extract({
fallback : 'style-loader',
use : [
'css-loader',
'sass-loader'
]
})
},
....
]
}
}

The way you include images in your component will determine if the consumer of your component will get them.

I include them in the css file using the content property. For example

.mySky{
width: 20px;
height: 20px;
content: url('../assets/images/thunderSky.png');
}

The issue I ran into was the relative paths of the images in the published CSS files were messed up. After a lot of searching, this article (also in the links below) helped.

Install the following:

npm install --save-dev file-loader url-loader

Update your webpack.config like this:

module.exports = {
...
module: {
rules: [
{
test: /\.(png|svg|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options:{
fallback: "file-loader",
name: "[name][md5:hash].[ext]",
outputPath: 'assets/',
publicPath: '/assets/'
}
}
]
},
...
resolve: {
alias:{
...
'assets': path.resolve(__dirname, 'assets')
}
}
]
}
}
  1. How to publish your package on npm(all about package.json)
  2. Publish Beta to NPM
  3. Exporting images via webpack (webpack.config.js)
  4. My full webpack configuration:
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const pkg = require('./package.json');
const path = require('path');
const libraryName= pkg.name;module.exports = {
entry: path.join(__dirname, "./src/index.js"),
output: {
path: path.join(__dirname, './dist'),
filename: 'myUnflappableComponent.js',
library: libraryName,
libraryTarget: 'umd',
publicPath: '/dist/',
umdNamedDefine: true
},
plugins: [
new ExtractTextPlugin({
filename: 'myUnflappableComponent.css',
}),
],
node: {
net: 'empty',
tls: 'empty',
dns: 'empty'
},
module: {
rules : [
{
test: /\.(png|svg|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options:{
fallback: "file-loader",
name: "[name][md5:hash].[ext]",
outputPath: 'assets/',
publicPath: '/assets/'
}
}
]
},
{
test: /\.*css$/,
use : ExtractTextPlugin.extract({
fallback : 'style-loader',
use : [
'css-loader',
'sass-loader'
]
})
},
{
test: /\.(js|jsx)$/,
use: ["babel-loader"],
include: path.resolve(__dirname, "src"),
exclude: /node_modules/,
},
{
test: /\.(eot|ttf|woff|woff2)$/,
use: ["file-loader"],
},
{
test: /\.(pdf|doc|zip)$/,
use: ["file-loader"],
}]
},
resolve: {
alias: {
'react': path.resolve(__dirname, './node_modules/react') ,
'react-dom': path.resolve(__dirname, './node_modules/react-dom'),
'assets': path.resolve(__dirname, 'assets')
}
},
externals: {
// Don't bundle react or react-dom
react: {
commonjs: "react",
commonjs2: "react",
amd: "React",
root: "React"
},
"react-dom": {
commonjs: "react-dom",
commonjs2: "react-dom",
amd: "ReactDOM",
root: "ReactDOM"
}
}
};
How to package your React Component for distribution via NPM (2024)
Top Articles
How to Avoid Coinbase Fees to Keep More of Your Profits? 
Are Lawsuit Settlements Taxable in California?
Chs.mywork
NYT Mini Crossword today: puzzle answers for Tuesday, September 17 | Digital Trends
The Largest Banks - ​​How to Transfer Money With Only Card Number and CVV (2024)
4-Hour Private ATV Riding Experience in Adirondacks 2024 on Cool Destinations
Http://N14.Ultipro.com
Phone Number For Walmart Automotive Department
Chalupp's Pizza Taos Menu
Zitobox 5000 Free Coins 2023
Www Thechristhospital Billpay
Mivf Mdcalc
Ktbs Payroll Login
4Chan Louisville
Obituary | Shawn Alexander | Russell Funeral Home, Inc.
Yesteryear Autos Slang
Rosemary Beach, Panama City Beach, FL Real Estate & Homes for Sale | realtor.com®
Premier Reward Token Rs3
Salem Oregon Costco Gas Prices
Shopmonsterus Reviews
Shiftselect Carolinas
Maxpreps Field Hockey
A Person That Creates Movie Basis Figgerits
Papa Johns Mear Me
New Stores Coming To Canton Ohio 2022
Goodwill Of Central Iowa Outlet Des Moines Photos
Wku Lpn To Rn
Craigslist Fort Smith Ar Personals
The Collective - Upscale Downtown Milwaukee Hair Salon
Cfv Mychart
LG UN90 65" 4K Smart UHD TV - 65UN9000AUJ | LG CA
Housing Assistance Rental Assistance Program RAP
Forager How-to Get Archaeology Items - Dino Egg, Anchor, Fossil, Frozen Relic, Frozen Squid, Kapala, Lava Eel, and More!
Save on Games, Flamingo, Toys Games & Novelties
Nacho Libre Baptized Gif
Whitehall Preparatory And Fitness Academy Calendar
20+ Best Things To Do In Oceanside California
Frcp 47
Daly City Building Division
Citibank Branch Locations In Orlando Florida
888-822-3743
Pathfinder Wrath Of The Righteous Tiefling Traitor
Petra Gorski Obituary (2024)
Elven Steel Ore Sun Haven
CrossFit 101
The Sports Academy - 101 Glenwest Drive, Glen Carbon, Illinois 62034 - Guide
Dicks Mear Me
House For Sale On Trulia
La Fitness Oxford Valley Class Schedule
Latest Posts
Article information

Author: Delena Feil

Last Updated:

Views: 6408

Rating: 4.4 / 5 (65 voted)

Reviews: 80% of readers found this page helpful

Author information

Name: Delena Feil

Birthday: 1998-08-29

Address: 747 Lubowitz Run, Sidmouth, HI 90646-5543

Phone: +99513241752844

Job: Design Supervisor

Hobby: Digital arts, Lacemaking, Air sports, Running, Scouting, Shooting, Puzzles

Introduction: My name is Delena Feil, I am a clean, splendid, calm, fancy, jolly, bright, faithful person who loves writing and wants to share my knowledge and understanding with you.