In this tutorial I will be showing you how to create an npm library that is composed of react component. This will definitely help you incase you want to reuse code in multiple projects or if you just want to create your own library.
Table of contents:
- Getting Started
- Creating the library
- Initializing the library
- Bundling the library
-
Editing
package.json
- Publishing
- Conclusion
If you are ready, lets get started.
Getting Started
First we shall initialize a react project. So got to your terminal and enter the directory that you want to create your project and create a new react app with the create-react-app
CLI.
npx create-react-app react-npm-library
## then enter the new directory
cd react-npm-library
## then start the dev server
yarn start
I recommend using npx
since it installs the latest versions of react-scripts
and does not install any thing globally.
Then enter the src
directory and create a new directory where you will place your component library
cd src
mkdir react-library ## you can name it any name
Creating the library
if you have done the above steps now you are ready to create you library. So now lets create a simple library that creates good buttons.
Inside the react-library
directory we shall create a file for the component.
touch button.jsx
touch index.css
Then place this code in button.jsx
import React, {useState, useEffect} from 'react';
import './button.css'
const AwesomeButton = (props) => {
const [color, setColor] = useState(null)
useEffect(() => {
if (props.variant) {
if (props.variant === 'primary') {
setColor('#0077ff')
} else if (props.variant === 'secondary') {
setColor('#ff0062')
} else if (props.variant === 'success') {
setColor('#0f8000')
} else {
setColor('#949393')
}
}
})
const {children} = props
return (
<button
className='buttonComponent'
style={{
backgroundColor: color
}}
>
{children.toUpperCase()}
</button>
)
}
export default AwesomeButton;
in index.css
.buttonComponent {
border-radius: 3px;
padding: 0.3rem 0.5rem;
cursor: pointer;
border: none;
transition: all .3s ease-out;
box-shadow: #272727b0 1px 1px 1px, #272727b0 -1px -1px 1px;
}
.buttonComponent:hover {
box-shadow: #272727b0 1px 1px 3px, #272727b0 -1px -1px 3px;
}
.buttonComponent:active {
opacity: .8;
}
Now you can import it from App.js
and test it. If it works well then lets move on.
Initializing the library
Now if it works so you have to enter the react-library
directory and create get it ready for publishing.
cd react-library
After then initialize an npm package
npm init -y
This will create a package.json
file in the root directory. It should look like this
{
"name": "react-library",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
Now you are ready to move on to the next section
Bundling the library
Now lets get ready to bundle the library. We shall do this in a few steps
Re-arranging the package directory
So now lets arrange the react-library
directory so it can be favorable for bundling.
Move to your terminal and type these commands inside the react-library
directory
mkdir src
move button.jsx src
move index.css src
cd src
torch index.js
The above commands will move the button.jsx
and index.css
files to a new src
directory and also create a new file called index.js
By now your project structure looks something like this.
|
- src
| - index.js
| - index.css
| - button.jsx
- package.json
Inside the index.js
file add the following code
import AwesomeButton from './button.js'
const returnLibrary = () => {
return {
AwesomeButton: AwesomeButton
// you can add here other components that you want to export
}
}
export default returnLibrary()
Installing bundlers
Now lets install the bundlers. I recommend rollup since I think it is easy to use when bundling libraries compared to webpack but if you want to use another bundler then you can use it.
So in the root of the react-library
directory install rollup.
Note: You have to install them as
devDependencies
by adding the--save-dev
flag. This is because they are used by you when in development mode else if you don't this will cause make your users to install them if you add them to thedependency
list
npm install rollup --save-dev
Rollup will be used to compile our code. But since we definitely might want to compile into es5 syntax so we shall have to install babel and a plugin to use with rollup. You should not that jsx
syntax is special and is not valid javascript so you should also install support for it.
So type the following commad int in the command line to install all required packages.
npm install @babel/cli @babel/core @babel/preset-env @babel/preset-react @rollup/plugin-babel --save-dev
Since we are also bundling css then we shall have to install a styles bundler for rollup we shall use rollup-plugin-styles
npm install rollup-plugin-styles autoprefixer --save-dev
Optional
We can also add babel runtime helpers. this is important if you are bundling a library with babel. So type this in the command line
npm install @babel/runtime
npm install @babel/plugin-transform-runtime --save-dev
If you want source maps then install this plugin to.
npm install rollup-plugin-sourcemaps --save-dev
Configuration
Now lets configure the rullop and babel for compiling our code.
In the root directory create these to files.
rollup.config.js
.babelrc
Inside rollup.config.js
add the following code.
import styles from "rollup-plugin-styles";
const autoprefixer = require('autoprefixer');
import { terser } from 'rollup-plugin-terser'
import babel from '@rollup/plugin-babel';
// the entry point for the library
const input = 'src/index.js'
//
var MODE = [
{
fomart: 'cjs'
},
{
fomart: 'esm'
},
{
fomart: 'umd'
}
]
var config = []
MODE.map((m) => {
var conf = {
input: input,
output: {
// then name of your package
name: "react-awesome-buttons",
file: `dist/index.${m.fomart}.js`,
format: m.fomart,
exports: "auto"
},
// this externelizes react to prevent rollup from compiling it
external: ["react", /@babel\/runtime/],
plugins: [
// these are babel comfigurations
babel({
exclude: 'node_modules/**',
plugins: ['@babel/transform-runtime'],
babelHelpers: 'runtime'
}),
// this adds sourcemaps
sourcemaps(),
// this adds support for styles
styles({
postcss: {
plugins: [
autoprefixer()
]
}
})
]
}
config.push(conf)
})
export default [
...config,
]
Also add this to .babelrc
{
"presets": [
"@babel/preset-react",
"@babel/preset-env"
]
}
Editing package.json
scripts
Now got to package.json
and edit the scripts section and change it to this.
// package.json
...
"scripts": {
"build": "rollup -c"
}
...
Build package
Now that everything is set run
npm run build
This will compile your package into the dist
directory.
Editing package.json
Now that our library has been built lets edit package.json
to make our library ready for publishing.
If you have followed from the beginning I think your packages.json
looks something like this.
{
"name": "react-library",
"version": "1.0.0",
"description": "",
"main": "index.js",
"directories": {
"test": "test"
},
"scripts": {
"build": "rollup -c"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@babel/runtime": "^7.12.5"
},
"devDependencies": {
"@babel/cli": "^7.12.10",
"@babel/core": "^7.12.10",
"@babel/plugin-transform-runtime": "^7.12.10",
"@babel/preset-env": "^7.12.11",
"@babel/preset-react": "^7.12.10",
"@rollup/plugin-babel": "^5.2.2",
"rollup-plugin-sourcemaps": "^0.6.3",
"rollup-plugin-styles": "^3.12.2",
}
}
I will explain what a few important feilds represent and you can find out more on at the npm documentation.
Fields
name
and version
If you plan to publish your package, the most important things in your package.json are the name and version fields as they will be required. The name and version together form an identifier that is assumed to be completely unique. Changes to the package should come along with changes to the version. If you don't plan to publish your package, the name and version fields are optional.
A name can be optionally prefixed by a scope, e.g. @myorg/mypackage.
decription
Put a description in it. It's a string. This helps people discover your package, as it's listed in npm search.
keywords
Put keywords in it. It's an array of strings. This helps people discover your package as it's listed in npm search.
homepage
The url to the project homepage.
license
You should specify a license for your package so that people know how they are permitted to use it, and any restrictions you're placing on it.
If you're using a common license such as BSD-2-Clause or MIT, add a current SPDX license identifier for the license you're using, like this:
{"license":"BSD-3-Clause"}
people fields: author, contributors
The "author" is one person. "contributors" is an array of people. A "person" is an object with a "name" field and optionally "url" and "email", like this:
{
"name":"Barney Rubble",
"email":"b@rubble.com",
"url":"http://barnyrubble.tumblr.com/"
}
Adding peerDependecies
Since we dont want to have react installed twice in the users projects but also need the user to have it we shall add it as a peerDependecies
so add this to you package.json file
"peerDependencies": {
"react": "^17.0.1",
"react-dom": "^17.0.1"
}
Final package.json
Now edit it to look like this
{
"name": "react-library",
"version": "1.0.0",
"description": "your description",
"main": "dist/index.cjs.js",
"scripts": {
"build": "rollup -c"
},
"peerDependencies": {
"react": "^17.0.1",
"react-dom": "^17.0.1"
},
"dependencies": {
"@babel/runtime": "^7.12.5"
},
"keywords": [
"react",
"keywords"
],
"author": "Your name",
"license": "MIT",
"devDependencies": {
"@babel/cli": "^7.12.10",
"@babel/core": "^7.12.10",
"@babel/plugin-transform-runtime": "^7.12.10",
"@babel/preset-env": "^7.12.11",
"@babel/preset-react": "^7.12.10",
"@rollup/plugin-babel": "^5.2.2",
"rollup-plugin-sourcemaps": "^0.6.3",
"rollup-plugin-styles": "^3.12.2",
}
}
Publishing
Now you are ready to publish.
First create an npm account if you don't have one.
Creating .npmignore
I hope by now your projects structure looks like this:
|
| - dist
| - index.esm.js
| - index.cjs.js
| - index.umd.js
| - src
| - index.js
| - index.css
| - button.jsx
| - .babelrc
| - package.json
| - rollup.config.js
But since we don't want to publish our source code to npm we only want to publish the compiled code then we will create a .npmignore
file that will prevent files we don't want to publish from being published
add this to .npmignore
file.
## the src folder
src
.babelrc
rollup.config.js
## node modules folder
node_modules
## incase you have a git repositiory initiated
.git
.gitignore
CVS
.svn
.hg
.lock-wscript
.wafpickle-N
.DS_Store
npm-debug.log
.npmrc
config.gypi
package-lock.json
Finding a name
Sometimes you might try to publish a package and find that the name is either already taken or the name is almost identical to another package so its better to first search and see if the package name is already taken. So type the following command in the command line.
npm search [package name]
if you find that nobody is using it them you can use the name.
Testing your package
To test your package your have to go to another projects on you computer and type
npm link /path/to/your/package
Adding README.md
You should also add a Readme.md
file that will be displayed on npm having a description of your package. You might be familiar with it if you have ever created a repository on GitHub
Publishing
If all works well then you can publish it by typing
npm publish
Conclusion
I hope this article has been helpful to you if you have any question just leave it in the comments section and all the source code can be found on Github
Top comments (19)
Is there any way to use the
react-scripts
toolchain to build a library? I mean CRA has already done everything for us so why set up rollup separately?We used CRA for previewing our component but rollup is the one in charge of bundling the library
اکسسوریهای قهوه که برای دستیابی به بهترین طعم و عطر قهوه، باید سرمایهگذاری کرد. قیمت اکسسوری قهوه اکسسوریهایی مانند بانک قهوه، ماگ قهوه، همزن قهوه و آسیاب قهوه از جمله لوازم ضروری هستند که هر علاقهمند به قهوهای باید داشته باشد. همچنین، خرید این لوازم جانبی قهوه از طریق فروشگاههای آنلاین، راحتتر و سریعتر است.
How to make this supporting SASS, CSS Modules?
I found an article online that I think can help u
Here is the link to the blog post
کادو براي اقايان
هديه دادن به آقايان ميتواند بسته به سليقه و علاقههاي فرد، متنوع باشد. در زير چند پيشنهاد براي کادوهاي مناسب براي آقايان آورده شده است:
ساعت مچي:
يک ساعت شيک و با کيفيت ميتواند يک هديه عالي باشد. کادو براي مرد متاهل برندهاي معروف و مدلهاي مختلفي وجود دارند که ميتوانيد بسته به سليقهي گيرنده انتخاب کنيد.
لوازم الکترونيکي:
گجتهاي الکترونيکي مانند گوشي هوشمند، هدفون بيسيم، تبلت، يا اسپيکر بلوتوثي هميشه محبوب هستند.
لوازم ورزشي:
اگر گيرنده به ورزش علاقهمند است، لوازم ورزشي مانند کفش ورزشي، لباس ورزشي، يا تجهيزات مخصوص ورزش مورد علاقهاش ميتواند انتخاب خوبي باشد.
Thanks for the tutorial I learn much.
You say to do "npm link" the react-library if wanting to test it in some React App locally.
What about this react-npm-library app we build in this tutorial, that host the react-library. Can I do npm link trying the library?
npm link
is for testing locally in react app in another directory but since thereact-library
is already inreact-npm-library
there is no need to link it. But if you link it will also workThanks it's working I publish to npm cool thanks.
But when I npm link I can't compile the exacts same code I think it's missing something in the rollup.config.js look here please:
stackoverflow.com/questions/694119...
When I do npm link and start server I get the error:
"Failed to compile.
src\react-library\dist\index.cjs.js
Line 11:261: Expected an assignment or function call and instead saw an expression no-unused-expressions"
Am I suppose to change in the package.json the "main": "dist/index.cjs.js", to this: "main": "src/index.js", ?
I Try that with the npm link in place and then get duplicate react hook error even the react-library use peerDependencies in package.json.
reactjs.org/warnings/invalid-hook-...
Please advice
If anyone else need an answer. Thanks I fixed it reading the docs
reactjs.org/warnings/invalid-hook-...
"This problem can also come up when you use npm link or an equivalent. In that case, your bundler might “see” two Reacts — one in application folder and one in your library folder. Assuming myapp and mylib are sibling folders, one possible fix is to run npm link ../myapp/node_modules/react from mylib. This should make the library use the application’s React copy.
Note"
Hey Beingana,
great Tutorial. Was exactly what I was looking for.
While reading through I noticed this
torch button.jsx
torch index.css
Was this maybe supposed to be
touch button.jsx
touch index.css
Again, thanks for the tutorial
Thank you... I will update that so soon.
Shouldn't
@babel/runtime
be included as apeerDependency
?import { terser } from 'rollup-plugin-terser'
what does terser do ??
Terser is a library that minifies code. This is good for production mode.
If you don't include that plugin then rollup will just compile your code without minifing it.
Thanks that really helped a lot
Thank you ... you save my time
Next episode support tsx ?
Is there a simple way to adapt this so that it would work with typescript?
Some comments may only be visible to logged-in visitors. Sign in to view all comments.