DEV Community

Cover image for How to Create and Publish a React Component Library

How to Create and Publish a React Component Library

Alex Eagleson on November 17, 2021

All code from this tutorial as a complete package is available in this repository. If you are interested in a video version of this tutorial, chec...
Collapse
 
nasheomirro profile image
Nashe Omirro

It's been almost a year and a half now after this post was written so there are a couple of errors that the article doesn't mention, I listed some of the ones I've encountered and the solutions:

  • rollup -c Failed to load Config... this is because rollup's config cannot support both import statements (esm) and require (cjs) simultaneously. You either have to use one or the other for every import inside your config file. If you want to use esm, first you need to rename your config file to rollup.config.mjs to tell rollup that it's a module then theres something extra you need to do for importing jsons:
import packageJson from "./package.json" assert { type: "json" };
Enter fullscreen mode Exit fullscreen mode
  • the @rollup/plugin-typescript package now needs tslib as a peer dependency, just download tslib as dev dependency and you should be good to go.
  • for optimization with terser, rollup-plugin-terser seems to be unmaintained and won't work in some of the latest versions of rollup, since this was a pretty heavily used package rollup came up with an official plugin, use @rollup/plugin-terser instead.
  • jest no longer has jsdom by default, to use the test environment, download jest-environment-jsdom as a dev dependency separately.
  • storybook with react and react-dom peer dependencies we're already mentioned but I wanted to mention my fix for it, so first is to download both react and react-dom as a dev dependency, then duplicate react to be a peer-dependency (this is surprisingly what other libraries do):
{
  "devDependencies": {
    "react": <version>,
    "react-dom": <version>
    // ...
  },
  "peerDependencies": {
    "react": <version>
    // ...
  }
}
Enter fullscreen mode Exit fullscreen mode
  • don't place react-dom as a peer dependency because we solely need that just for storybook and our library doesn't use it anywhere. and just in case we accidentally include it in our bundle we need to mark the library as external on our rollup config file, react is already excluded because of the peerDepsExternal plugin.
export default [
  {
    input: "src/index.ts",
    external: ['react-dom'],
    // ...
  }
]
Enter fullscreen mode Exit fullscreen mode

That's all of the problems I had to deal with but I did skip some steps (adding SCSS and CSS) so there's probably some stuff I missed.

Collapse
 
alexeagleson profile image
Alex Eagleson

Much appreciated for this, and the time and effort that went into it. I've updated the article and placed links to this comment where appropriate for the things that have changed in the past year.

Thanks again, and cheers!

Collapse
 
alek2499 profile image
Alek2499

I'm not sure if you would look at this comment but i really like this tutorial but the only problem is that the project i'm doing right now doesn't require typescript and i'm not sure how to do this replacing ts with js, this is the only tutorial which looks workable please guide me and help me solve it. Thank you!

Collapse
 
amanksdotdev profile image
Aman 🚀

When running rollup -c if anyone is getting SyntaxError: Unexpected Identifier error than just upgarde your nodejs version to v18 LTS, I was on 16.3 so the importing json using the assert keyword was causing this. Thanks to @nasheomirro for providing a link to a blog where it states that nodejs 17.5+ required.

Collapse
 
rinconcamilo profile image
Camilo Rincon

I updated to v18 LTS however, if you are using nvm and you are still receiving an error such as SyntaxError: Unexpected token '.' then you will want to update your nvm to the latest, delete the v18 and reinstall. Hope this helps someone

Collapse
 
roguealien profile image
Rodrigo García

version 16.19.0 works just fine... just in case anyone needs to work with 16 version

Collapse
 
cyrfer profile image
John Grant

For me, I had to add this:
external: [...Object.keys(packageJson.peerDependencies || {})],

Because I had more dependencies than only react & react-dom. I had several:

  "peerDependencies": {
    "@emotion/react": "^11.10.6",
    "@emotion/styled": "^11.10.6",
    "@mui/material": "^5.11.12",
    "@types/react": "^18.0.28",
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  }
Enter fullscreen mode Exit fullscreen mode
Collapse
 
mperudirectio profile image
mperudirectio • Edited

following these steps partially solved my issues. Unfortunately, however, after changing the rollup configuration file, when I launch the script this error occurs:

[!] RollupError: Could not resolve "./components" from "dist/esm/types/index.d.ts".

but if i look in the dist bundled folder actually everything seems ok with imports

SOLVED
what I had to do was make sure that the rollup.config.mjs file was entirely written IN ES and then add the brackets to the default dts object which previously with the "dirty" file gave me an error if I added them, but instead now adding them (plugins: [dts()]) with the well written file the build completes correctly.

Collapse
 
zahidshowrav profile image
Zahid R. Showrav • Edited

@mperudirectio, I don't understand you solution partially, I am getting same RolleupError as you got. my rollup.config.mjs file is written entirely in ES, but I am not getting the later part of your solution. Can you please make it clear, where to add the brackets?

Collapse
 
olodocoder profile image
Adams Adebayo

Oh good!

Collapse
 
zahidshowrav profile image
Zahid R. Showrav • Edited

@nasheomirro In my case, following your steps I am still getting this error:
RollupError: Could not resolve entry module "dist/esm/types/index.d.ts".

SOLVED
The error was solved by simply adding a property "rootDir": "src" into the tsconfig.json file within the compilerOptions object.
Hope this helps someone who is having this problem.

Collapse
 
rajahn1 profile image
Rafael Jahn

thank you!

Thread Thread
 
danyalaslam profile image
Danyal Aslam

The solution stated above is awesome and resolved all the issues, although if someone if trying to implement it after November 2023, they may encounter the following error

template-react-component-library@0... rollup
rollup -c

[!] Error: Cannot find package 'rollup-plugin-postcss' imported from /Users/danyalaslam/Desktop/ReactProjects/template-react-component-library/rollup.config.mjs

Solution : need to install updated rollup-plugin-postcss library

and import it as a default in rollup.config.mjs
import terser from "@rollup/plugin-terser";

Then hit the command
npm run rollup

Collapse
 
nguyenhoanglam1402 profile image
Liam Hoang Nguyen

Many thanks for your effort to update this article

Collapse
 
aayani profile image
Wahaj Aayani

Well written and very concise. But on a side note, has any one came across the issue while doing $ rollup -c?

Error: Could not resolve entry module (dist/esm/types/index.d.ts)

Collapse
 
alexeagleson profile image
Alex Eagleson

That command means "run rollup with the configuration found in rollup.config.js"

So I would start by making sure your config file looks exactly like the example and all the dependencies are installed

From this section:

dev.to/alexeagleson/how-to-create-...

Collapse
 
walachian profile image
Ovidiu-Deliu

I've stumbled upon the same issue. It is an issue with the rollup/plugin-typescript that has been also reported here:

github.com/rollup/plugins/issues/1230

You can follow the instructions there to get a workaround or just do the workaround from here:
github.com/alexeagleson/template-r...

Collapse
 
salimzade profile image
Teymur Salimzade

just change dist/esm/types/index.d.ts -> dist/esm/index.d.ts

Collapse
 
smitterhane profile image
Smitter
Collapse
 
trandinhhiep789 profile image
eltr

Hi Wahaj Aayani
Did you fixed it?

Collapse
 
zakariaels profile image
Zakaria EL AISSAOUI • Edited

Have you found a fix to this issue ?

Collapse
 
trandinhhiep789 profile image
eltr
Thread Thread
 
zakariaels profile image
Zakaria EL AISSAOUI

Thank you :)

Collapse
 
idontchop profile image
Nathan Dunn

Could be skipped the first step, making a component to export.

Collapse
 
charlescollignon profile image
Charles

Hi ! Thank you a lot for this article and video, it helps me a lot !
I may have found something useful if you want to use external librairies (like material UI, HighchartJS, etc) and do some custom components in your npm package (storybook).

The error

Unhandled Runtime Error Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:

  1. You might have mismatching versions of React and the renderer (such as React DOM)
  2. You might be breaking the Rules of Hooks
  3. You might have more than one copy of React in the same app

The why

The React of the parent project is being used, instead of using the second, that is a duplicate React.

The solution

I found a solution (here and here) where you just have to add this line inside the rollup.config.js file :

external: ["react", "react-dom"],

Collapse
 
pavellaptev profile image
Pavel Laptev

That's really helpful. Thanks!

Collapse
 
blueberrychopsticks profile image
blueberrychopsticks

My fix was simply moving react and react-dom to peerDependencies in package.json

Collapse
 
alexeagleson profile image
Alex Eagleson

Good stuff, thanks for the resource!

Collapse
 
harshdeepjft profile image
Harshdeep Saiyam

Thank you so much for sharing the solution!!!

Collapse
 
trandinhhiep789 profile image
eltr

That's really helpful. Thanks!

Collapse
 
heypano profile image
Pano Papadatos

Awesome article!
Just in case this helps anyone:

I decided to use yarn instead of npm and ran into this issue --

[!] (plugin typescript) Error: @rollup/plugin-typescript: Could not find module 'tslib', which is required by this plugin. Is it installed?
Error: @rollup/plugin-typescript: Could not find module 'tslib', which is required by this plugin. Is it installed?

which was resolved by adding tslib as a dev dependency

Collapse
 
wallyatkins profile image
Wally Atkins
Collapse
 
ilamin profile image
Lamin

fix using npm i -D tslib

Collapse
 
keentdev profile image
Keanu Kent B. Gargar

got the same error even when using just npm

Collapse
 
tamb profile image
Tamb

I've been programming in JS for 7 years and this is one of the best tutorials I have ever taken. You explained the reason behind each configuration perfectly and I actually am excited to use React again.

Collapse
 
alexeagleson profile image
Alex Eagleson

Thank you! Glad it helped

Collapse
 
paul_emechebe profile image
Paul-Simon Emechebe

How do you test it before publishing

Collapse
 
returnvoid profile image
Gonzalo Geraldo

Excellent! followed the whole thing and in a couple of hours already have a package on github that I started using in another project (just for testing). Also added jest for unit testing and storybook. All working as expected, thanks!

Collapse
 
lwazevedo profile image
Luiz Azevedo

Hi Alex!

Congratulations for the tutorial and the beautiful explanation.
After a long time of your tutorial, it is still possible to apply it in a simple way.

I'm currently getting a Warning when I run npm start of a project created with create-react-app referring to the package created from your tutorial.
I've been looking for a solution but haven't found it yet.

When running npm start from the application that is using my package I get this warning:

warning in ./node_modules/my_package/dist/esm/index.js

Module Warning (from ./node_modules/source-maploader/dist/cjs.js)

Failed to parse source map from '.../components/my_component.tsx' file: Error ENOENT: no such file or directory, open '.../components/my_component.tsx' @ ./src/index.tsx

I know it's just a warning, but I wanted to resolve it.

Thanks.

Collapse
 
pheromona13 profile image
pheroMona13 • Edited

You should just add 'src' to the 'files' in your package.json so it will be included in your published folder for souremaps to work properly without error.

{
  ...
  "files": [
    "dist",
    "src"
  ],
  ...
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
lyubosumaz profile image
Lyuboslav Ilkov • Edited

Not Alex, but.
Did faced the same problem as you. I don't think we get sourcemaps working following the guide and this error is actually handaled to be displayed in the react-scripts v5 above.
My solutions:

  1. use "react-scripts": "4.0.2" or
  2. you can fix sourcemaps via @rollup/plugin-sucrase and rollup-plugin-sourcemaps like here and here dev install these libs and do few changes in rollup.config.mjs add on top:
import sourcemaps from 'rollup-plugin-sourcemaps';
import sucrase from '@rollup/plugin-sucrase';
Enter fullscreen mode Exit fullscreen mode

add in plugins:

[
   sourcemaps(),
...
   sucrase({
       exclude: ['node_modules/**'],
       transforms: ['typescript', 'jsx'],
   }),
]
Enter fullscreen mode Exit fullscreen mode

plugins order matters: depsExternal > resolve > sourcemaps > commonjs > typescript > postcss > sucrase > terser - is doing the trick for me. Executing rollup --config will throw warning and you can add:

onwarn(warning, warn) {
    if (warning.code === 'THIS_IS_UNDEFINED') return;
    warn(warning);
},
Enter fullscreen mode Exit fullscreen mode

like in here
Much love to Alex, it's a great guide <3

Collapse
 
williamfenu profile image
William Ferreira Nunes

@lyubosumaz, I fallowed this steps and I could build. However when I init the app which has the dependency for my lib I got that react is not defined. Did you faced something similar?

Collapse
 
malithranasing6 profile image
Malith Ranasinghe • Edited

create new react app and install your custom npm package.
then,
Create an environment variables file named .env in the project root and add GENERATE_SOURCEMAP=false to it.

OR

Run npm i -D cross-env and then in the package.json file update the start script to the following:

"scripts": {
"start": "cross-env GENERATE_SOURCEMAP=false react-scripts start"
}

Collapse
 
malithranasing6 profile image
Malith Ranasinghe

or comment out sourcemap in rollup.config.js file

{
file: packageJson.main,
format: "cjs",
** // sourcemap: true,**
},
{
file: packageJson.module,
format: "esm",
** // sourcemap: true,**
},

Collapse
 
xm2020 profile image
xingming2020

I have the same warning. Did you fix it?

Collapse
 
jay__6f0d370ab80c134d48f2 profile image
Jay

Thanks for this article. I just recently created a pirate library for my job. This article looks spot on.

The one thing that helped me, I didn’t want to post all my project, and when you do an npm publish from the root, it posts your whole project. So what I do is copy my package,json, and node modules into the dist folder, cd into it, and do my npm publish from there. It makes a much cleaner finished product.

Collapse
 
lalilunar profile image
Laura

Hey, how did you use a private package in another project? I created a private repo/package and I tried to use it in another project but got 404 and can't find the way around to fix it and use it!
Thanks in advance!

Collapse
 
andrew3009 profile image
Andrew

Hi, love the post.
One problem for me is that @testing-library/react is throwing loads of errors, mainly that it can't be found or that it also requires react-dom and most of the time also says that it can't find react-dom even when it's installed.
I've found the package react-test-renderer runs without any issues just incase anyone sees this and are having the same issues.

Also to get storybook to work 3 more packages needed to be installed. @mdx-js/react assert @storybook/addon-docs

Collapse
 
matthewdean profile image
Matthew Dean

Note that this rollup.config.js file no longer works in the latest versions of rollup. It will assume it is a CommonJS with that extension at first. However, if you change it to .mjs as the command line suggests, then it will fail again, because of the use of require in the file.

At this point, you could try adding --bundleConfigAsCjs to the rollup script, but that will fail with "dts is not a function".

What finally solved this for me was renaming to .mjs and removing the package.json require statement and just using strings to define the esm / cjs outputs.

Collapse
 
rodrigonovais profile image
Rodrigo Tolentino de Novais

It's quite ugly but you can use dts.default() instead of dts() as a alternative

Collapse
 
abhinavanshul profile image
abhinav anshul

Amazing article,however I'm stuck into an issue here
when I try to install the package, it says not found (it is a public repo), neither its available on the npmjs website.

I've written down about my problem here too : stackoverflow.com/questions/706391...

Collapse
 
alexeagleson profile image
Alex Eagleson

This tutorial is specifically for publishing a package on Github, I would imagine you could probably publish a public package to npmjs.com as well but I've never personally done that before so I'm not sure the exact steps to follow.

If you figure it out and share I'd be happy to update the tutorial

Cheers

Collapse
 
ppulwey profile image
Patrick Pulwey

I've wrote a sample lib and some instructions based on this tutorial, with a quick introductions how to publish to npm. The steps are almost the same. You can find it here: github.com/ppulwey/remake-lib#publish

Collapse
 
bhatvikrant profile image
Vikrant Bhat

I am facing the same issue, did you stumble upon a way to publish to NPM as well?

Collapse
 
gavinsykes profile image
Gavin Sykes

It looks like you can only really do one or the other at a time, I'm yet to give this a go but this article seems to cover it - sevic.dev/npm-publish-github-actions/

Collapse
 
ben_the_husi profile image
Ben

I really appreciate that you've summarised this process.

I wonder what would you recommend as a workflow to actually author those components?

What I mean is that I'd never develop a design system in isolation. I usually need my whole app/website to see the context and make adjustments if necessary, but - obviously - I don't want to include any of that into the final commit of the design system.

So what do you recommend to seamlessly bring together the development of a main application and the belonging design system locally?

Collapse
 
alexeagleson profile image
Alex Eagleson • Edited

That's a great question, I want to make sure I'm understanding it properly.

I can only speak personally, since we work with a designer, all of our design work is done in advance in Figma so the overall picture & theming of everything can be designed together, then I turn those designs into components and use Storybook to validate that the components match the intention of the designs.

If I were working alone or without a designer I would probably create a few helper components that are meant to group components together (but not actually exported as part of the library). Something that would allow me to wrap unrelated components and compare them in proximity with one another with some padding between. Something along the lines of the Stack from Material-UI (which is basically just a convenient wrapper around a flex element). Then I could drop all my components inside of it and use Storybook to make a story where I can see them.

That said, since I am not skilled at design (and don't pretend to be) I would definitely encourage anyone else who has an opinion on this to respond as well!

Cheers.

Collapse
 
ben_the_husi profile image
Ben

Thanks Alex sharing your take on this.

I also encourage everyone to read this article from the authors of Tailwind CSS / UI.

They've explained their process of designing components and then turning them into code.

Their high level overview with your practical article I think is a good starting point.

Collapse
 
mynameisdu profile image

Thank you for your great tutorial, It was very enlightening to me, But I have a little of different view, I think deleting package-lock.json will cause the package version to get out of control, It is a bad practice, Perhaps we can use npm i PAKEAGE_NAME@x.x.xto solve the release problem

Collapse
 
alexeagleson profile image
Alex Eagleson

That's definitely a great option if it works. Deleting package-lock.json in a mature application absolutely creates a risk of introducing bugs with package interactions, even between minor and patch versions.

That said, although removing the file may be bad practice, I would be interested to see if your solution works for initializing Storybook in an existing project and creating a stable alignment between all the peerDependencies. That's the issue that gets fixed with a fresh npm install and I'm not sure if harcoding the versions of each package fixes that issue (I'd be happy to be wrong though).

Collapse
 
92srdjan profile image
WingsDevelopment • Edited

Hello Alex, great blog!
I managed to publish my package on github (public), and I can see it here: github.com/WingsDevelopment/export...
But I am not able to install it, I am getting error 404, not found..
npm ERR! code E404
npm ERR! 404 Not Found - GET registry.npmjs.org/@wingsdevelopme... - Not found
npm ERR! 404
npm ERR! 404 '@wingsdevelopment/export-poc3@0.0.1' is not in this registry.
npm ERR! 404 You should bug the author to publish it (or use the name yourself!)

Can you help me with this please?

Collapse
 
alexeagleson profile image
Alex Eagleson

A 404 not found GET error usually means a permissions issue. Are you trying to install it from the same machine you published from?

Because you published to Github privately, the machine that is installing it needs to have a Github token with access permission in its npmrc file, same as the one that did the publishing

If you want to be able to use your package publicly on any machine, then publish it to NPM instead of github (just look up their instructions for that, it's even easier than publishing privately)

Collapse
 
92srdjan profile image
WingsDevelopment

I just figured this out, there were two problems, I had to remove dist from .gitignore and command copied from github: npm install @wingsdevelopment/export-poc3@0.0.2 didn't work, so i changed it to npm install wingsdevelopment/export-poc3 and it worked!
I am not sure why I had to remove dist from gitignore and push it to the github repo ?

Thread Thread
 
alexeagleson profile image
Alex Eagleson

You should not, it's very likely that is a symptom of an unrelated problem. There is no scenario where including a build folder in a repo is the right decision. I could recommend circling back and checking every step of the process to ensure you have configured correctly

Good luck!

Thread Thread
 
lalilunar profile image
Laura

hi, I'm having issues trying to use my private repo/package! I tried to look for a solution but I couldn't find it , please help me if you can!

Thread Thread
 
olegfilonchuk profile image
Oleg Filonchuk

Hi, I found out that your github username should be spelled in lowercase everywhere you use it e. g. .npmrc

Collapse
 
kbradley profile image
Kristin Bradley • Edited

Great article! Very clear and easy to follow.

When importing the library I created into my test application, it worked great except I got this error: TS7016: Could not find a declaration file for module ... implicitly has an 'any' type.

It looks like I need to declare the module for my components but I'm unsure where to do this? I want to add this in the component library itself so users importing it won't see this error.

Collapse
 
alexeagleson profile image
Alex Eagleson • Edited

The output of the declaration files is covered in the "Adding Rollup" section. From this part of the rollup config in particular:

  {
    input: "dist/esm/types/index.d.ts",
    output: [{ file: "dist/index.d.ts", format: "esm" }],
    plugins: [dts()],
  },
Enter fullscreen mode Exit fullscreen mode
Collapse
 
kbradley profile image
Kristin Bradley

Ok thanks. I double checked and I have that in my rollup.config.js file.

Collapse
 
geraldjtorres profile image
Gerald Jeff Torres

HI Alex,

This is an amazing tutorial but I ran into a problem after installing storybook.
I was not able to run 'npm run rollup' because of the new storybook files and folders'.

Am I missing anything in the rollup.config.js file that will ignore these files from being bundled?

Collapse
 
alexeagleson profile image
Alex Eagleson

Hey there, I don't believe that anything in the rollup config was impacted by the installation of storybook. What was the error specifically? You can try cloning the repo linked at the top of the tutorial which has a working version all combined together, and then begin to compare that with your implementation piece by piece to see where the difference might lie.

Collapse
 
mdrayer profile image
Michael Drayer

Hey Alex,

I was running into the same issue, even using your repository. On npm run rollup, I was getting the following warnings:

src/index.ts → dist/cjs/index.js, dist/esm/index.js...
(!) Plugin typescript: @rollup/plugin-typescript TS4082: Default export of the module has or is using private name 'ButtonProps'.
src/stories/Button.stories.tsx: (7:1)

  7 export default {
    ~~~~~~~~~~~~~~~~
  8   title: 'Example/Button',
    ~~~~~~~~~~~~~~~~~~~~~~~~~~
... 
 13   },
    ~~~~
 14 } as ComponentMeta<typeof Button>;
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Enter fullscreen mode Exit fullscreen mode

While the package built out fine, it also included components and type files for the various Storybook and test files within the src directory, for example dist/esm/types/stories/Button.d.ts, dist/esm/types/components/Button/Button.stories.d.ts and dist/esm/types/components/Button/Button.test.d.ts were all created. My solution was to add test and stories files to the exclude arg in the typescript() plugin:

typescript({
  exclude: [
    // Exclude test files
    /\.test.((js|jsx|ts|tsx))$/,
    // Exclude story files
    /\.stories.((js|jsx|ts|tsx|mdx))$/,
  ],
  tsconfig: "./tsconfig.json",
}),
Enter fullscreen mode Exit fullscreen mode

There are probably other ways to exclude these files from the rollup bundler, but this worked for me. Thanks for the tutorial by the way! I've been in webpack land for a while, so it's interesting to see how other bundlers are being used.

Thread Thread
 
orahul1 profile image
Rahul Raveendran • Edited

I think we can also add files to exclude in the tsconfig.json file

{
  "compilerOptions": {
  },
  "exclude": [
    "src/stories",
    "**/*.stories.tsx"
  ]
}
Enter fullscreen mode Exit fullscreen mode

I don't know which one is better. This solution also worked for me

Collapse
 
wesleyjanse profile image
Wesley Janse

Great post,

Got a small issue, running the tests though, You mentioned something
about not adding React-Dom to the library since that would deny the purpose of building a 'standalone' component library, if I remember correctly.

Now when I try to run 'yarn test' I'm getting the following error.

Cannot find module 'react-dom' from 'node_modules/@testing-library/react/dist/pure.js'

This is fixed when I add React-Dom to the project.

Do you see any way around this?

Collapse
 
alexeagleson profile image
Alex Eagleson

In the "Adding Tests" section there is an instruction for editing your jest config to use jsdom instead of React DOM which is aimed to eliminate the need for that dependency, did you include that in your config?

Collapse
 
wesleyjanse profile image
Wesley Janse

Yes I have that included, I have just added React-Dom as a peer dep, as a workaround. It is accessing the config file correctly though, just always wants to go back to ReactDom, but the workaround works, and since we need it anyway for storybook it's fine.

Thanks again for the blog!

Collapse
 
darkmattermatters profile image
SuperSegfault • Edited

I also have the same issue, only fixed by installing react-dom. It's probably worth mentionning that I did not include the Storybook part in my library and I did not publish on npm. But apart from that it's the same config files.
Also, moving react to peerDependencies does not work with Jest as he seems to need them in order to run the tests (because of the import in Button.test.tsx). Without Jest the packaging seems to work though.

Collapse
 
ac303 profile image
AC303

Hey Alex - Great post! I'm setting up a component library for my team and I'm using this article as a guideline. I need to provide documentation for the next developer to integration test the npm package locally within a consumer app on their machine, as a way to validate their changes to the npm package, before pushing the package to npm. Use case:

1 - I cloned the repo at the url that you provided
2 - I ran npm install on the root folder
3 - I ran npm run rollup on the root folder
4 - I copied the dist folder created by npm run rollup from your app to my-test-app under the src dir
5 - I ran the following command from the command line at the root of my-test-app:
npm install --save ./src/dist
6 - After step 5, your package reflects in my-new-app node_modules as @YOUR_GITHUB_USERNAME/YOUR_REPOSITORY_NAME
7 - I imported the component in App.js and intellisense resolves it without error:
import {Button} from "@YOUR_GITHUB_USERNAME/YOUR_REPOSITORY_NAME";

However, when I npm start my-new-app, the following error is displayed in the console and app:

Module not found: Error: Can't resolve '@YOUR_GITHUB_USERNAME/YOUR_REPOSITORY_NAME' in 'C:\my-test-app\src'

Any idea what the issue might be or how to resolve? I did a lot of research online and found some potential matches but no working fix

Collapse
 
brunofrigeri profile image
Bruno Frigeri

Were you able to solve this issue?

Collapse
 
samiulhsohan profile image
Samiul H. Sohan • Edited

Great tutorial, @alexeagleson !

I am facing a problem. Whenever I'm using things like useState in the library and then importing in my Next.js project, it's giving me error.

TypeError: Cannot read properties of null (reading 'useState')

error

And if I move react inside the peerDependencies, it shows this error:

error2

Collapse
 
matthiasnannt profile image
Matthias Nannt

Hi @samiulhsohan
I faced the same problem and found a solution. This is what causes the error:
blog.maximeheckel.com/posts/duplic...

And this is my next.config.js that solves the issue:

var path = require("path");

module.exports = {
  webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
    config.resolve.alias["react"] = path.resolve("./node_modules/react");
    return config;
  },
};
Enter fullscreen mode Exit fullscreen mode
Collapse
 
lsjesus profile image
Lucas Silva de Jesus

Hi @samiulhsohan!

I'm facing the same problem in my react aplication. Did u solved it??

Collapse
 
neznaykagm profile image
Neznayka

I've done everything exactly like in your guide. However, the bundle of my library, along with the source, weighs 30 megabytes. I've tried many things and can't figure out what the problem is.

Here's my rollup.config.mjs:

import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import typescript from "@rollup/plugin-typescript";
import postcss from "rollup-plugin-postcss";
import {dts} from "rollup-plugin-dts";
import terser from '@rollup/plugin-terser';
import peerDepsExternal from 'rollup-plugin-peer-deps-external';
import packageJson from "./package.json" assert { type: "json" };
import preserveDirectives from "rollup-plugin-preserve-directives"

export default [
  {
    input: "src/index.ts",
    output: [
      {
        file: packageJson.main,
        format: "cjs",
        sourcemap: false,
      },
      {
        file: packageJson.module,
        format: "esm",
        sourcemap: false,
      },
    ],
    onwarn(warning) {
      if (warning.code === 'MODULE_LEVEL_DIRECTIVE') return;

      // Log the warning to the console
      console.warn(`Rollup warning: ${warning.message}`);
    },
    plugins: [
      peerDepsExternal(),

      resolve(),
      commonjs(),
      typescript({ tsconfig: "./tsconfig.json" }),
      postcss(),

      terser(),
      preserveDirectives()
    ],
  },
  {
    input: "dist/esm/types/index.d.ts",
    output: [{ file: "dist/index.d.ts", format: "esm" }],
    plugins: [
      dts()
    ],
    external: [/\.css$/],
  },
];
Enter fullscreen mode Exit fullscreen mode

Here's my tsconfig.json:

{
  "$schema": "https://json.schemastore.org/tsconfig",
  "compilerOptions": {
    "target": "es5",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true,

    "jsx": "react",
    "module": "ESNext",
    "declaration": true,
    "declarationDir": "types",
    "sourceMap": true,
    "outDir": "dist",
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "emitDeclarationOnly": true,

    "paths": {
      "@/*": ["./src/components/Editor/*"]
    }
  },
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
annmarieswitzer profile image
Annmarie Switzer

Alex - this is an awesome tutorial. Thank you so much! I was wondering if you could tell me how to export a CSS file to be used by a consuming application. For example, I want to define a color palette in the library and allow my consumers to use those pre-defined CSS vars. How could I do that?

my-react-library/src/palette.css

:root {
    --grey0: #eaeaea;
}
Enter fullscreen mode Exit fullscreen mode

Consuming application's index.css

@import 'my-react-library/src/palette.css'

:root {
    --custom-white: var(--grey0);
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
annmarieswitzer profile image
Annmarie Switzer

I may have been overthinking this. What I have done is simply copied palette.css directly into /dist - no need for rollup to be involved. Now, my consuming application can import that file like so:

@import '~my-react-library/dist/palette.css'

:root {
  --custom-white: var(--grey0);
}
Enter fullscreen mode Exit fullscreen mode

and this works perfectly.

Collapse
 
kaptn3 profile image
Victoria Kapitonenko

Hi! How you get it? I have dist without css, I need too themes file for import to other project

Collapse
 
walachian profile image
Ovidiu-Deliu

For the Jest part, while attempting to run the tests script you might encounter the error:

Test environment jest-environment-jsdom cannot be found. Make sure the testEnvironment configuration option points to an existing node module.

Configuration Documentation:
jestjs.io/docs/configuration

As of Jest 28 "jest-environment-jsdom" is no longer shipped by default, make sure to install it separately.

As the error says, the package for jest-environment-jsdom isn't bundled anymore with the base jest library, so you'll have to install it separately with:

npm install jest-environment-jsdom --save-dev

Happy coding ;)

Collapse
 
gavinsykes profile image
Gavin Sykes • Edited

Thanks for this Alex, it's been incredibly helpful and encouraged me to finnaly take the plunge and stop using webpack to (badly) bundle my components and libraries together!

One issue I am facing though, which I'm hoping someone has encountered and fixed before, is that when I try and import the main component from my package into another project, TypeScript then shouts at me "JSX element type 'YourComponent' does not have any construct or call signatures. ts(2604)". Like, what?

I've tried changing the return type from ReactElement to JSX.Element to ReactNode to ReactChild to FunctionComponent to even removing it altogether, and it just flat out refuses to get rid of that error message.

Collapse
 
gavinsykes profile image
Gavin Sykes

False alarm!

My component wasn't set as a default import (so I'm going to look at doing that) so changing import MyComponent from '@gavinsykes/my-component' to import { MyComponent } from '@gavinsykes/my-component fixed it! 😁

Collapse
 
iankm profile image
Roomak • Edited

Hey alex! Thank you so much for such a clear summary of the component library creation process. I followed your advice but can't seem to test this with a local project.

My component library is housed in the local directory "component-library". I used npm link ../component-library in my project. When I try to important Button from my component library, I get:

Error: Can't resolve './Button' in 'component-library/dist/esm/types/components'

Any advice on this? I've double checked my file structure versus yours at least 20 times.

Collapse
 
keentdev profile image
Keanu Kent B. Gargar

Once you used "npm link" on your component library, also make sure to include that linked npm on your main app by using "npm link NAME_OF_YOUR_COMPONENT_PACKAGE", name is indicated on your component library' package.json name.

Collapse
 
kiranparajuli589 profile image
Kiran Parajuli

Why do I get the src folder inside the types folder inside dist?

This is the folder structure I get after hitting rollup -c rollup.config.ts --configPlugin typescript

Image description

The rollup config:

import { RollupOptions } from "rollup";
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import dts from "rollup-plugin-dts";

import pkg from './package.json' assert { type: "json" };

const config: RollupOptions[] = [
    {
        input: "src/index.ts",
        external: ["react-dom"],
        output: [
            {
                file: pkg.main,
                format: "cjs",
                sourcemap: true,
            },
            {
                file: pkg.module,
                format: "esm",
                sourcemap: true,
            }
        ],
        plugins: [
            resolve(),
            commonjs(),
            typescript({
                tsconfig: "./tsconfig.json",
            })
        ]
    },
    {
        input: "dist/esm/types/src/index.d.ts",
        output: [{ file: "dist/index.d.ts", format: "esm" }],
        plugins: [dts()],
    }
]

export default config
Enter fullscreen mode Exit fullscreen mode

Typescript Config:

{
    "compilerOptions": {
        "module": "esnext",
        "declaration": true,
        "declarationDir": "types",
        "sourceMap": true,
        "outDir": "dist",
        "moduleResolution": "node",
        "emitDeclarationOnly": true,
        "resolveJsonModule": true,
        "allowSyntheticDefaultImports": true,
        "esModuleInterop": true,
        "lib": [
            "dom",
            "esnext",
            "scripthost",
        ]
    },
    "include": [
        "src",
        "eslint.config.mjs",
        "rollup.config.ts"
    ]
}
Enter fullscreen mode Exit fullscreen mode

Enter fullscreen mode Exit fullscreen mode
Collapse
 
neznaykagm profile image
Neznayka

I have the same issue. After extensive searches for a solution, I found nothing better than to resolve this issue by simply deleting the 'types' directory.

use "rollup-plugin-delete":
plugins: [
dts(),
del({ hook: "buildEnd", targets: ["dist/esm/types", "dist/cjs/types"] })
],

Collapse
 
edgarcodes profile image
Edgar Carrillo • Edited

I also ran into a issue when I ran npm run storybook. It would get to 70% completion and then stop completely. What fixed it was adding:

"typescript": { reactDocgen: 'react-docgen' }

To the .storybook/main.js folder

Collapse
 
oscarcornejo profile image
Oscar Cornejo Aguila

Hi Alex, very good publication, but I was left with a question, how can I work or include sass in my component library?

I would appreciate the help!

Collapse
 
alexeagleson profile image
Alex Eagleson • Edited

Great question, and it took a bit of time to figure out.

Getting SCSS to work with the rollup bundle is extremely easy. All you need to do from the current state of the tutorial is update rollup.config.js in the dts() plugin config section. Change the external value from just CSS to:

external: [/\.(css|less|scss)$/],

With that alone you can now change Button.css to Button.scss and import Button.scss in your Button component. That's it! The Postcss plugin for Rollup that is already installed will take care of it for you.

Getting it to work with Stoyrbook however is another beast altogether and involves re-installing Storybook using the Webpack 5 option instead of the default webpack 4. More than I can fit in a comment here, but I may update the blog post itself.

I have however updated the repository with those changes, so it should work out of the box with Storybook if you clone it now.

Cheers.

Collapse
 
oscarcornejo profile image
Oscar Cornejo Aguila

Thanks Alex, I was really lost on how to add sass to my component library, and although I followed the steps that storybook recommends, I had not realized that the errors that kept happening were due to the webpack version, thank you very much for this update. 👏👏
Greetings from Chile! 🙌

Collapse
 
alexeagleson profile image
Alex Eagleson

New SCSS section has been added to the post.

Collapse
 
farihaaftab0 profile image
FarihaAftab0 • Edited

Will you please tell how to make it work with css modules.. how to resolve module.scss extension?

Thread Thread
 
alexeagleson profile image
Alex Eagleson

Simply add modules: true on your Rollup postcss plugin. There's a section for modules on the plugin NPM docs:

npmjs.com/package/rollup-plugin-po...

Collapse
 
smitterhane profile image
Smitter

Am speaking as of Sep. 2022. I came across errors with the loaders style-loader@3.1.1 and css-loader@6.5.1 when i run npm run storybook. After some time hacking my ways, I came to a finding that these loaders need webpack version >=5. So I had to initialize storybook with npx sb init --builder webpack5

Collapse
 
d0natellll0 profile image
d0natellll0

Hello, i'm learning web dev and i'm trying to follow this article.
I would like to adapt it, and create a simple component without typescript, but i can't find a way to do it...
I have a simple project with "vanilla" react inside and i don't know yet Typescript (i will :D )

Can somebody help me ?
Your explainations are great btw.

Thank you for this content.

Collapse
 
dariorega profile image
Dario Regazzoni

Hey @alexeagleson , first of all thank you for this awesome tutorial, i've learnt a lot !

Could i ask you some help on some errors i got after trying out to expand this base.

I've added more components (2) on the library, so my index.ts in the components folder is looking like that now:

export { default as HVButton } from "./Atoms/HVButton"; // as your example
export { default as HVTag } from "./Atoms/HVTag"; // new one
export { default as HVRating } from "./Molecules/HVRating"; // new one
Enter fullscreen mode Exit fullscreen mode

As i recall in your video, you mentioned that it should look like this when you want to have more components.

I am a bit more worried about two erros that i got when i import the HVTag and and the HVRating.

HVTag is a dump component, and only use Styled components behind the scene, here is the error:

1. react.development.js:1659 Uncaught TypeError: Cannot read properties of null (reading 'useDebugValue')
2. Warning: Invalid hook call. 
Enter fullscreen mode Exit fullscreen mode

HVRating is a component that have some useState hooks inside, and the errors are the following when importing him :

1. Uncaught TypeError: Cannot read properties of null (reading 'useState')
2. Warning: Invalid hook call
Enter fullscreen mode Exit fullscreen mode

Do you have any clues/obvious mistake that i might have done that come to your mind by reading this ?

Thank you for the time taken reading my question, and have a wonderful day!

In case, the repository is there: github.com/DarioRega/component-lib...

Also, I've added tailwindcss and styled components as well, regarding tailwind i did only an extra config for storybook to pick it up (and it works).

However when importing my Button that has tailwind classes on the main project, styles don't appears, but not a big issue, i can investigate that.

Collapse
 
grantralls profile image
Grant Ralls

I've been looking for a way to create a component library. I've configured my own webpack projects before but I constantly ran into errors with both bit.dev and create-react-library. Thank you for putting this together!

Collapse
 
yav0602 profile image
Yerokhin Arthur

Excellent article!!! But I am facing an issue, when I use some component from my library in my own app (created via "create-react-app" cli), it throws a warning, that webpack can't find a source map :(

Image description

Collapse
 
zafarsaleem profile image
Zafar Saleem

This seems like a different issue with the import. I am exporting the component as mentioned in the blog when I import it, it gives me "JSX element type 'Button' does not have any construct or call signatures." which needs to be typed to make TS aware of it is a call signature. I tried few possible solutions such using "React.FC" etc however, I still get this error.

One more thing is that I updated the code of the button to use local state and change the state using onClick event which it does not seem to work. I do this because there will be use cases where a button needs to handle some local states instead of "lifting the state up". Like I might need to use context API which will handle component level scope etc. Here is how my simple button component looks like.

import React from "react";

export interface ButtonProps {
  label: string;
}

const Button = (props: ButtonProps) => {
  const [state, setState] = React.useState('');

  React.useEffect(() => {
    console.log(state);
  }, [state]);

  return (
    <button onClick={() => {
      alert('clicked');
      setState('state updated');
    }}>
      {props.label}
    </button>
  )
};

export default Button;
Enter fullscreen mode Exit fullscreen mode

Here is how I import it.

import React from 'react';
import Button from '@github-username/test-lib';

function App() {
  return (
    <div className="App">
      <Button label="Label" />
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

The label prop is working fine. It renders "Label" string however, the onClick event does not get fired and also I get below error.

Image description

Collapse
 
arantebw profile image
Billy Arante • Edited

I think you need to check the dist directory, to see if the modules were transpiled properly.

Collapse
 
fdiskas profile image
Vytenis

Readme, license and packge.json will be included by default. To check what will be available as you publish your package you can run npm pack

Collapse
 
neznaykagm profile image
Neznayka

I've done everything exactly like in your guide. However, the bundle of my library, along with the source, weighs 30 megabytes. I've tried many things and can't figure out what the problem is.

Here's my rollup.config.mjs:

import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import typescript from "@rollup/plugin-typescript";
import postcss from "rollup-plugin-postcss";
import {dts} from "rollup-plugin-dts";
import terser from '@rollup/plugin-terser';
import peerDepsExternal from 'rollup-plugin-peer-deps-external';
import packageJson from "./package.json" assert { type: "json" };
import preserveDirectives from "rollup-plugin-preserve-directives"

export default [
  {
    input: "src/index.ts",
    output: [
      {
        file: packageJson.main,
        format: "cjs",
        sourcemap: false,
      },
      {
        file: packageJson.module,
        format: "esm",
        sourcemap: false,
      },
    ],
    onwarn(warning) {
      if (warning.code === 'MODULE_LEVEL_DIRECTIVE') return;

      // Log the warning to the console
      console.warn(`Rollup warning: ${warning.message}`);
    },
    plugins: [
      peerDepsExternal(),

      resolve(),
      commonjs(),
      typescript({ tsconfig: "./tsconfig.json" }),
      postcss(),

      terser(),
      preserveDirectives()
    ],
  },
  {
    input: "dist/esm/types/index.d.ts",
    output: [{ file: "dist/index.d.ts", format: "esm" }],
    plugins: [
      dts()
    ],
    external: [/\.css$/],
  },
];
Enter fullscreen mode Exit fullscreen mode

*Here's my tsconfig.json: *

{
  "$schema": "https://json.schemastore.org/tsconfig",
  "compilerOptions": {
    "target": "es5",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true,

    "jsx": "react",
    "module": "ESNext",
    "declaration": true,
    "declarationDir": "types",
    "sourceMap": true,
    "outDir": "dist",
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "emitDeclarationOnly": true,

    "paths": {
      "@/*": ["./src/components/Editor/*"]
    }
  },
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
iamtjgoutham profile image
Goutham T J

@alexeagleson I have a problem, where the library has scss and the styles are getting applied in storybook, but in consumed project, the styles are not getting applied.
my rollup config is given below. Not sure what am I missing here.
and in the distribution folder as well, I don't see the scss files are processed.

any help is appreciated

import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import typescript from "@rollup/plugin-typescript";
import dts from "rollup-plugin-dts";

// import postcss from "rollup-plugin-postcss";
import scss from 'rollup-plugin-scss'

// minifying module
import terser from "@rollup/plugin-terser";
import peerDepsExternal from 'rollup-plugin-peer-deps-external';

const packageJson = require("./package.json");

export default [
  {
    input: "src/index.ts",
    output: [
      {
        file: packageJson.main,
        format: "cjs",
        sourcemap: true,
      },
      {
        file: packageJson.module,
        format: "esm",
        sourcemap: true,
      },
    ],
    plugins: [
      // minifying module
      peerDepsExternal(),

      resolve(),
      commonjs(),
      typescript({ tsconfig: "./tsconfig.json" }),

      // css
      scss({
        outputStyle: 'compressed'
      }),

      // minifying module
      terser()
    ],
  },
  {
    input: "dist/esm/types/index.d.ts",
    output: [{ file: "dist/index.d.ts", format: "esm" }],
    plugins: [dts.default()],

    // css config
    external: [/\.(css|less|scss)$/],
  },
];
Enter fullscreen mode Exit fullscreen mode
Collapse
 
bongvn profile image
bongvn

Hi! Awesome tutorial, very thorough and was able to get things working on my published version, however, I am having trouble resolving these errors when trying to test my package locally through yarn pack and link.

ERROR
Cannot read properties of null (reading 'useContext')
TypeError: Cannot read properties of null (reading 'useContext')
    at Object.useContext (http://localhost:3000/static/js/vendors-node_modules_mui_icons-material_ControlPointDuplicate_js-node_modules_mui_icons-mater-b27b5b.chunk.js:87839:25)
    at useInRouterContext (http://localhost:3000/static/js/vendors-node_modules_mui_icons-material_ControlPointDuplicate_js-node_modules_mui_icons-mater-b27b5b.chunk.js:81767:45)
    at useLocation (http://localhost:3000/static/js/vendors-node_modules_mui_icons-material_ControlPointDuplicate_js-node_modules_mui_icons-mater-b27b5b.chunk.js:81781:4)
    at MainPage (http://localhost:3000/static/js/src_sections_creator-management_TalentDiscoverPage_pages_index_tsx.chunk.js:31862:82)
    at renderWithHooks (http://localhost:3000/static/js/bundle.js:622813:22)
    at mountIndeterminateComponent (http://localhost:3000/static/js/bundle.js:626099:17)
    at beginWork (http://localhost:3000/static/js/bundle.js:627395:20)
    at HTMLUnknownElement.callCallback (http://localhost:3000/static/js/bundle.js:612405:18)
    at Object.invokeGuardedCallbackDev (http://localhost:3000/static/js/bundle.js:612449:20)
    at invokeGuardedCallback (http://localhost:3000/static/js/bundle.js:612506:35)

ERROR
Cannot read properties of null (reading 'useReducer')
TypeError: Cannot read properties of null (reading 'useReducer')
    at useReducer (http://localhost:3000/static/js/vendors-node_modules_mui_icons-material_ControlPointDuplicate_js-node_modules_mui_icons-mater-b27b5b.chunk.js:87847:25)
    at useToastContainer (http://localhost:3000/static/js/vendors-node_modules_mui_icons-material_ControlPointDuplicate_js-node_modules_mui_icons-mater-b27b5b.chunk.js:83251:70)
    at ToastContainer (http://localhost:3000/static/js/vendors-node_modules_mui_icons-material_ControlPointDuplicate_js-node_modules_mui_icons-mater-b27b5b.chunk.js:83909:28)
    at renderWithHooks (http://localhost:3000/static/js/bundle.js:622813:22)
    at mountIndeterminateComponent (http://localhost:3000/static/js/bundle.js:626099:17)
    at beginWork (http://localhost:3000/static/js/bundle.js:627395:20)
    at HTMLUnknownElement.callCallback (http://localhost:3000/static/js/bundle.js:612405:18)
    at Object.invokeGuardedCallbackDev (http://localhost:3000/static/js/bundle.js:612449:20)
    at invokeGuardedCallback (http://localhost:3000/static/js/bundle.js:612506:35)
    at beginWork$1 (http://localhost:3000/static/js/bundle.js:632380:11)
Enter fullscreen mode Exit fullscreen mode

Here's what my rollup.config.js file looks like:

import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import typescript from "@rollup/plugin-typescript";
import dts from "rollup-plugin-dts";
import packageJson from "./package.json" assert { type: "json" };
import peerDepsExternal from "rollup-plugin-peer-deps-external";

export default [
  {
    input: "src/index.ts",
    output: [
      {
        file: packageJson.main,
        format: "cjs",
        sourcemap: true,
      },
      {
        file: packageJson.module,
        format: "esm",
        sourcemap: true,
      },
    ],
    plugins: [
      peerDepsExternal(),
      resolve(),
      commonjs(),
      typescript({ tsconfig: "./tsconfig.json" }),
    ],
    external: [...Object.keys(packageJson.peerDependencies)],
  },
  {
    input: "dist/esm/types/src/index.d.ts",
    output: [{ file: "dist/index.d.ts", format: "esm" }],
    plugins: [dts()],
    external: [
      /\.css$/,
      "@types/react",
      "@types/react-dom",
      "@types/react-router-dom",
      "@types/react-beautiful-dnd",
      "@mui/styled-engine",
      "@emotion/react",
      "@emotion/styled",
      "@mui/material",
      "@mui/icons-material",
      "@material-ui/core",
      "@mui/styles",
      "tslib",
    ],
  },
];
Enter fullscreen mode Exit fullscreen mode

Here's what I have under peerDependencies:

"peerDependencies": {
    "@apollo/client": "^3.6.9",
    "@babel/core": "^7.23.2",
    "@babel/preset-env": "^7.23.2",
    "@babel/preset-react": "^7.22.15",
    "@emotion/core": "^11.0.0",
    "@emotion/react": "^11.5.0",
    "@emotion/styled": "^11.3.0",
    "@material-ui/core": "^4.12.3",
    "@mui/icons-material": "^5.10.3",
    "@mui/material": "~5.10.5",
    "@mui/styled-engine-sc": "^6.0.0-alpha.1",
    "@mui/styles": "~5.10.3",
    "@rollup/plugin-commonjs": "^25.0.7",
    "@rollup/plugin-node-resolve": "^15.2.3",
    "@rollup/plugin-terser": "^0.4.4",
    "@rollup/plugin-typescript": "^11.1.5",
    "@types/js-cookie": "^3.0.2",
    "@types/jwt-decode": "^3.1.0",
    "@types/node": "^12.0.0",
    "@types/react": "^17.0.0 || ^18.0.0",
    "@types/react-beautiful-dnd": "^13.1.4",
    "@types/react-dom": "^18.0.0",
    "@types/react-router-dom": "^5.3.3",
    "graphql": "^16.5.0",
    "js-cookie": "^3.0.1",
    "jwt-decode": "^3.1.2",
    "logrocket": "^5.0.1",
    "react": "^17.0.0 || ^18.2.0",
    "react-beautiful-dnd": "^13.1.0",
    "react-dom": "^17.0.0 || ^18.2.0",
    "react-error-overlay": "6.0.9",
    "react-router-dom": "^6.4.0",
    "react-scripts": "5.0.1",
    "react-toastify": "^8.1.0",
    "rollup": "^4.1.4",
    "rollup-plugin-dts": "^6.1.0",
    "rollup-plugin-peer-deps-external": "^2.2.4",
    "styled-components": "^6.1.0",
    "tslib": "^2.6.2",
    "typescript": "~4.8.4",
    "web-vitals": "^1.0.1",
    "xlsx": "^0.18.5"
  },
  "peerDependenciesMeta": {
    "react": {
      "optional": true
    },
    "react-dom": {
      "optional": true
    },
    "react-router-dom": {
      "optional": true
    },
    "react-beautiful-dnd": {
      "optional": true
    },
    "react-scripts": {
      "optional": true
    },
    "react-toastify": {
      "optional": true
    },
    "styled-components": {
      "optional": true
    },
    "@types/react": {
      "optional": true
    },
    "@types/react-dom": {
      "optional": true
    },
    "@types/react-router-dom": {
      "optional": true
    },
    "@types/react-beautiful-dnd": {
      "optional": true
    },
    "@emotion/react": {
      "optional": true
    },
    "@emotion/styled": {
      "optional": true
    },
    "@emotion/core": {
      "optional": true
    },
    "@material-ui/core": {
      "optional": true
    },
    "@mui/icons-material": {
      "optional": true
    },
    "@mui/material": {
      "optional": true
    },
    "@mui/styled-engine-sc": {
      "optional": true
    },
    "@mui/styles": {
      "optional": true
    }
  },
Enter fullscreen mode Exit fullscreen mode

Thanks!

Collapse
 
hoangsuy profile image
Phạm Huy Hoàng

Help! After i run the command npm run rollup i get the following error:
(node:4008) ExperimentalWarning: Import assertions are not a stable feature of the JavaScript language. Avoid relying on their current behavior and syntax as those might change in a future version of Node.js.
(Use node --trace-warnings ... to show where the warning was created)
(node:4008) ExperimentalWarning: Importing JSON modules is an experimental feature and might change at any time

src/index.ts → dist/cjs/index.js, dist/esm/index.js...
! SyntaxError: Unexpected token (14:7) in D:\Vscode\ui-kit-solution\node_modules\react-native\index.js
node_modules/react-native/index.js (14:7)
12:
13: // Components
14: import typeof AccessibilityInfo from './Libraries/Components/AccessibilityInfo/AccessibilityInfo';
^
15: import typeof ActivityIndicator from './Libraries/Components/ActivityIndicator/ActivityIndicator';
16: import typeof Button from './Libraries/Components/Button';
at Parser.pp$4.raise (D:\Vscode\ui-kit-solution\node_modules\rollup\dist\shared\rollup.js:22880:13)
at Parser.pp$9.unexpected (D:\Vscode\ui-kit-solution\node_modules\rollup\dist\shared\rollup.js:20088:8)
at Parser.pp$9.expect (D:\Vscode\ui-kit-solution\node_modules\rollup\dist\shared\rollup.js:20082:26)
at Parser.pp$8.parseImportSpecifiers (D:\Vscode\ui-kit-solution\node_modules\rollup\dist\shared\rollup.js:21211:8)
at Parser.parseImport (D:\Vscode\ui-kit-solution\node_modules\rollup\dist\shared\rollup.js:26457:32)
at Parser.pp$8.parseStatement (D:\Vscode\ui-kit-solution\node_modules\rollup\dist\shared\rollup.js:20264:49)
at Parser.pp$8.parseTopLevel (D:\Vscode\ui-kit-solution\node_modules\rollup\dist\shared\rollup.js:20145:21)
at Parser.parse (D:\Vscode\ui-kit-solution\node_modules\rollup\dist\shared\rollup.js:19917:15)
at Function.parse (D:\Vscode\ui-kit-solution\node_modules\rollup\dist\shared\rollup.js:19967:35)
at Graph.contextParse (D:\Vscode\ui-kit-solution\node_modules\rollup\dist\shared\rollup.js:26119:38)

Collapse
 
asiurobr profile image
Alberto Siurob

I faced with TypeError: pluginTerser.terser is not a function

I sorted out as this way

import terser from '@rollup/plugin-terser'

instead

import { terser } from "rollup-plugin-terser";

Remember rollup-plugin-terser is not longer maintained, use @rollup/plugin-terser instead.

Collapse
 
icep0ps profile image
icep0ps • Edited

How to fix Invalid hook call in storybook

If you are using storybook and you encounter this error:

Unhandled Runtime Error Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app

Don't panic. This is a common issue that can be solved by following these steps.

The root cause of this error is having duplicate React installs in your project, as explained here.

To check if you have more than one React, run npm ls react and see if you get multiple entries.

How to solve this

  • Remove react and react-dom from your devDependencies and dependencies and add them to peerDependencies in your package.json file. This will tell other packages that depend on React to use the same version as your project. For example:
"peerDependencies": {
 "react": "^18.2.0", 
"react-dom": "^18.2.0" 

} 

Enter fullscreen mode Exit fullscreen mode
  • In your rollup.config.js file, make sure you add this line just below your input: 'src/index.ts' to prevent bundling React twice:

 export default [ 
{
 input: 'src/index.ts', external: ['react', 'react-dom'] 
} 
];

Enter fullscreen mode Exit fullscreen mode
  • Delete node_modules and any .lock files e.g package.json.lock or yarn.lock and restart your IDE.

  • Run yarn install or npm install and once it's done, the error should be gone :) I HIGHLY RECOMMEND YOU USE YARN BECAUSE IT LOOKS LIKE THE package.json.lock HAS SOME PROBLEMS

Collapse
 
jpizzatiuvl profile image
Jorge Pizzati • Edited

Thanks again for this tutorial!

I haven't faced any particular issue with the setup you propose. However, I would like to enable rollups tree-shaking feature by adding the flag "preserveModules: true" in the output config. The problem I have is that by doing so, when I run "rollup -c" I encounter the following error:

Image description

This is my rollup config:
Image description

Any help is very much appreaciated!

Collapse
 
jpizzatiuvl profile image
Jorge Pizzati • Edited

This tutorial is just what I was looking for, thanks @alexeagleson this post is gold!

For anyone still following this tutorial and is getting the following error when running rollup -c:

[!] RollupError: Node tried to load your configuration as an ES module even though it is likely CommonJS. To resolve this, change the extension of your configuration to ".cjs" or pass the "--bundleConfigAsCjs" flag.
Enter fullscreen mode Exit fullscreen mode

This issue is caused due to breaking changes on updated deps and you can fix it by modifying how you import the package.json inside the rollup.config.js as specified here: Importing package.json in Rollup 3

Collapse
 
leongeldsch profile image
Leon Geldschläger

I'm thinking about adding TailwindCSS to my library for ease of use and smaller bundle size, and I was wondering if that's a good idea. Do you know if the 'purging' of unused styles still works with components that are imported and not created locally? And if it does, do you have tips for implementing it?

Collapse
 
aburaihan profile image
Abu Raihan • Edited

Hi @alexeagleson I have just copied all configs from yours. But I am still getting this error. The types folder in dist/esm is not creating
Image description

After building

Image description

Collapse
 
kaptn3 profile image
Victoria Kapitonenko • Edited

Hi! Perfect tutorial, thanks a lot!

I'm currently getting warnings when I run yarn start project created with create-react-app referring to the package created from your tutorial.
I've been looking for a solution but haven't found it yet.

I get this warnings:

LOG from webpack.FileSystemInfo
<w> Managed item /my-app/node_modules/@ui/dist isn't a directory or doesn't contain a package.json (see snapshot.managedPaths option)
<w> Managed item /my-app/node_modules/@ui/package.json isn't a directory or doesn't contain a package.json (see snapshot.managedPaths option)
+ 12 hidden lines
Enter fullscreen mode Exit fullscreen mode

Have you solution for solve this warnings?

Collapse
 
gerry profile image
Gerry

Hey mate this is amazing tutorial. All worked as you explained but I have two issues. if you can give me some insight I would appreciate it.

  • When I import my components to a react project, I always get the errors below for my each component.

WARNING in ./node_modules/testtest-component/lib/components/upload/index.js
Module Warning (from ./node_modules/source-map-loader/dist/cjs.js):
Failed to parse source map from '/Users/john/Documents/Codes/test/node_modules/testtest-component/src/components/upload/index.ts' file: Error: ENOENT: no such file or directory, open '/Users/john/Documents/Codes/test/node_modules/testtest-component/src/components/upload/index.ts'

  • my component size is 1 mb but when I import it to a react project I can see about 50 mb on my network tab. Instead of my component if I even add antd design it loads about 2 mb resources. do you have any idea about this?

Image description

Thanks in advance

Collapse
 
lionberg profile image
Lionberg • Edited

Is it a library for only one component?..
How can I export two components, for example:

I have created an Svg component inside components folder.

Then how do I export it in main index.ts?..

export { default as Button } from "./Button";
// how to export Svg, too?..
Enter fullscreen mode Exit fullscreen mode
Collapse
 
skrtz profile image
stephen kurtz

Almost made it to the end but I am receiving this error when I try to run Storybook. Anyone dealing with the same message?

[Error: EBUSY: resource busy or locked, open 'C:\Users\skrtz\Documents\Projects\React\template-react-component-library\node_modules\.cache\storybook\dev-server\325c8f456729b912b0d2134054eb7448-41ac79ddc5290d504ad69ef1fe8200a7'] {
  errno: -4082,
  code: 'EBUSY',
  syscall: 'open',
  path: 'C:\\Users\\skrtz\\Documents\\Projects\\React\\template-react-component-library\\node_modules\\.cache\\storybook\\dev-server\\325c8f456729b912b0d2134054eb7448-41ac79ddc5290d504ad69ef1fe8200a7'
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
skrtz profile image
stephen kurtz • Edited

Solved, but Idk if it's a long term solution:
I added this code to .storybook/main.js

module.exports = {
    managerWebpack: (config, options) => {
        options.cache.set = () => Promise.resolve();
        return config;
    }
}
Enter fullscreen mode Exit fullscreen mode

Original solution was found here: github.com/storybookjs/storybook/i...

Collapse
 
yav0602 profile image
Yerokhin Arthur

Thank you for this article! Excellent!!!
But I am facing such issue on my react app (created via "create-react-app" cli), if I use some component from my library, it throw warning, that webpack can't load source map :(

Image description

Collapse
 
evertondenis profile image
Everton Denis

Hi Alex, thanks for this article!
I've a question;
How I import the components using:
import { Button } from '@my_github/template-react-component-library';
or
import Button from '@my_github/template-react-component-library/Button';

I'm trying some strategies but without success.

Collapse
 
genghiskhan123 profile image
GenghisKhan123

Thank you for this great tutorial. You publish some really wonderful stuff on React here. I'm very happy I found your work on this blog: dailybuinaryhub.com.
Your resources have really helped me in my journey as a beginner.

Collapse
 
tyler_beggs_c97e0527f4e97 profile image
Tyler Beggs

Thanks for this.

Collapse
 
seanmclem profile image
Seanmclem

How different would this be for react native components?

Collapse
 
alexeagleson profile image
Alex Eagleson • Edited

Good question! Considering React Native supports the same NPM package installation process, my assumption would be that it's no different at all, you would simply need to identify in your package description that React Native is the target.

I haven't built RN packages before though so I could be wrong, I'm be curious if anyone else had input.

Collapse
 
sirdeon profile image
Deon Achuo • Edited

Image description

Please i get this error when i run the rollup command, Please i greatly need your help @alexeagleson

Collapse
 
talrodin profile image
Alyona Rodin

Thank you for article. It's 2022, December and it's unbelievable hard to follow, could not finish even the first part. Errors on each step. If there are so many updates, can someone please update the article also. Or at least add additional comments at the end of the article.

Collapse
 
smviula profile image
Sérgio Viúla

Did a similar thing, but gitsubmodule instead.
Its nice see such a detailed article, this was spot on 👍

Collapse
 
jmfgameiro profile image
Lagoagest

Great article, it helped me a lot to make my own libraries.

Maybe you can help to take one more step on the subject. I've been trying to implement multi-language on the libraries, but I don't seem to be able to pass the translations within the library into the App.

Have you ever implemented multi-language on libraries?

Collapse
 
foxter111 profile image
Andrey Kulikov • Edited

Hi Alex. I have a problem with my rollup config (I think so, but it isn't exactly). I am creating library with this tutorial. I use styled-component inside my library. When I connect it in my Next app, I have a problem with SSR render. When the page loads I see the components blinking because the styles are not being applied to the components on the server. Do you know, how I can solve this problem? Thank you)

Collapse
 
gokulravi1598 profile image
gokulravi1598

@alexeagleson

I'm facing some issues while I try to run the storybook. can you help me resolve that

Image description

Collapse
 
ssijak profile image
Saša Šijak

very good post, thank you

Collapse
 
alexeagleson profile image
Alex Eagleson

you are welcome

Collapse
 
ppulwey profile image
Patrick Pulwey

Really great! Thanks for sharing your findings and knowledge. Installing storybook is a bit tricky. I've wrote a quick README along with a working React@18 library that already contains storybook. It follows your tutorial @alexeagleson
Maybe it will help someone else - sharing is caring github.com/ppulwey/remake-lib

Collapse
 
federicoragazzoni profile image
Federico Ragazzoni

A really great article, clear and explanatory!
Wanting to go a step further, if I include material-ui or react-bootstrap dependencies (as peerDependencies) in my component library I always have these errors in my react application:

Warning: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:

  1. You might have mismatching versions of React and the renderer (such as React DOM)
  2. You might be breaking the Rules of Hooks
  3. You might have more than one copy of React in the same app See reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem. printWarning @ react.development.js:2318 ___________________ Uncaught TypeError: Cannot read properties of null (reading 'useContext') at Object.useContext (react-jsx-runtime.development.js:665:1) at Button (react-dom.production.min.js:313:1) at renderWithHooks (BreadcrumbItem.js:34:1) at updateForwardRef (Dropdown.js:168:1) at beginWork (Nav.js:53:1) at HTMLUnknownElement.callCallback (react-dom.development.js:19261:1) at Object.invokeGuardedCallbackDev (react-dom.development.js:19310:1) at invokeGuardedCallback (react-dom.development.js:19373:1) at beginWork$1 (named-references.js:1:1) at performUnitOfWork (events.js:84:1)

Do you have some suggestion about?
Thanks!

Collapse
 
alexeagleson profile image
Alex Eagleson

Yes. I'll start by saying even though ive done it before multiple times i STILL get errors I have trouble resolving when trying to create component libraries around existing ones like MUI. not sure why it needs to be so complicated but such as it is.

Yours is a common error and unfortunately it's one of those things that has nothing to do with what the error says. I'm sure the way you are using your hook is perfectly valid.

The cause of that error message is almost always from trying to load more than one copy of the React library. So for example if you have a project that has React as a dependency, but uses a library that also bundles the entire React library inside of itself. That's why we use "peerDependencies" in this demo, so that React/ReactDOM get installed with the user's app rather than being bundled with our package.

My suggestion would be to take a look at your dependencies in package.json and make sure that React & ReactDOM are peerDependencies rather than dependencies in the package you are building. Running "yarn install" or "npm install --legacy-peer-deps" will still install them in your node_modules folder, but not bundle them with your published version.

If using rollup lookup the "external" field in the rollup config and add all your react & mui packages to the "external" list to be absolutely sure they aren't included in your bundle

You can use this plugin as well to accomplish the same thing without having to set them explicitly in your config:

npmjs.com/package/rollup-plugin-pe...

Collapse
 
goldjns profile image
GoldJns • Edited

With Rollup 3.15.0 the following error occured: RollupError: Invalid value for option "output.file" - when building multiple chunks, the "output.dir" option must be used, not "output.file"
I renamed rollup.config.js to rollup.config.mjs and passed --bundleAsCjs as an option.
Did someone also came accross this problem?

Collapse
 
ananyadhananjaya profile image
ax • Edited

Hi. I'm getting this warning -> Failed to parse source map from 'C:\Users\anany\projects\template-test\node_modules\@ananyadhananjaya\react-template-component-library\src\components\Button\Button.tsx' file: Error: ENOENT: no such file or directory, open 'Cnts\Button\Button.tsx'

where am I going wrong here. Thanks!

Collapse
 
arash1381 profile image
Arash

That was AWESOME! Thank you. Is it the same process if I wanna publish my library on Bitbucket?
Wondering what needs to be put for below (instead of github):

{
"name": "@YOUR_GITHUB_USERNAME/YOUR_REPOSITORY_NAME",
"publishConfig": {
"registry": "npm.pkg.github.com/YOUR_GITHUB_USE..."
},
...

}

Collapse
 
okafohenry profile image
Okafor Henry • Edited

A very explanatory and easy-to-follow guide.
Although, when I try to test my newly built package in a demo project, nothing displays then I get this error...
what am I doing wrong? how do I fix it?

Image description

Collapse
 
wahidn profile image
WahidN

I am getting the following error when trying to use the library
Module not found: Can't resolve '../node_modules/style-inject/dist/style-inject.es.js'

Collapse
 
mattlang profile image
Matthew Lang • Edited

May I ask what verison of node you are using to have this run? When running rollup I get the error "Cannot use import statement outside a module".

I cloned this repository and also get the same error out the box, which makes me think it's a Node version issue or something like that. I've tried on v14.18.3 and v16.14.0

Collapse
 
harrisonhenri profile image
Harrison Henri dos Santos Nascimento

Hey Guys, first thanks for this wonderful article @alexeagleson! Is there anyone having problems when testing the main project using the library as a dependency? Have anyone had to mock the library in the tests of the main project?

Collapse
 
abhinavanshul profile image
abhinav anshul

Hi, just a question - if i'm publishing this to github package, why this would be available at npmjs at all?

Collapse
 
alexeagleson profile image
Alex Eagleson

npm (the tool) is still used to install it even though it is hosted privately on Github. It will not be visible on npmjs.com/ (the public package repository).

Collapse
 
alexeagleson profile image
Alex Eagleson

Great tips, thank you!

Collapse
 
hanifmhd profile image
Muhammad Hanif

Hi Alex,

Thanks for your tutorial about rollup, it's really help me to create react library!
Btw i want to ask about assets (image, font, etc), i cannot build it using rollup, can you help me?

Image description

Collapse
 
albrnawe profile image
dhme

hi Muhammad,
i would like to know if you found any ways to build assets like images, cuz im also want to include static images with my library

Collapse
 
negreanucalin profile image
Negreanu Calin

Thanks for your time writing and making the video, love it!!!

Collapse
 
alexeagleson profile image
Alex Eagleson

Glad it was helpful!

Collapse
 
ajay_compute profile image
Ajay Kumar

Hi Alex
Thanks for this , it has proved to be a lifesaver. Can you please explain how to test the library locally by using it in some other project before publishing it. Will npm link is the right way to go about this.

Collapse
 
preciouzword profile image
Preciouzword

Nice post. I enjoyed the read. I will like to work on something like this >> momsall.com/app-controlled-christm...

Collapse
 
imfaizanahmed profile image
Faizan Ahmed

Thanks for sharing this helpful article. I have published my first library. I am only facing an issue is that whenever I tried using any react-hook, published the library, and use that in my project it throws error.
Error: Cannot read properties of null (reading 'useRef')

Collapse
 
zakariaels profile image
Zakaria EL AISSAOUI • Edited

Can someone tell me how to bundle all my css and less files (they contains some css variables) ?
thanks

Collapse
 
sh4yy profile image
Shayan

Alex, this has been a great! Thank you very much.
Would you be interested in joining a dev community on discord? discord.gg/zvE8QjdWaf

Collapse
 
paul_emechebe profile image
Paul-Simon Emechebe

How do you test your components before publishing?

Collapse
 
alexeagleson profile image
Alex Eagleson
Collapse
 
zakariaels profile image
Zakaria EL AISSAOUI

Hello,
I have an issue described in stackoverflow: stackoverflow.com/questions/735805...

Can someone help please ?

Collapse
 
harshan89 profile image
Harshan Morawaka

Best article ever read, everything works for me.

Collapse
 
vandercloak profile image
Nick

Encountered: Cannot use import statement outside a module

I solved it by adding "type": "module", to the package.json file.

Collapse
 
anujssstw profile image
Anuj Singh

can we use hooks and stuff. in THat particular component??

Collapse
 
hanifmhd profile image
Muhammad Hanif

I think you should run npx create-react-app . first in your initial project, then add typescript there

Collapse
 
cagriuysal profile image
Cagri UYSAL

Thank you so much, you saved my life <3

Collapse
 
hyperdanish profile image
HyperDanisH

How can we remove the username or orgainzation name from the name of the library?

Collapse
 
daftpaul profile image
Paul Tang

Well written article. I have a question How can you test locally the library of components. I've tried with npm link but it seems that has problem about peer-deps with react

Collapse
 
paul_emechebe profile image
Paul-Simon Emechebe

Ive been trying to fix this.

Image description

Collapse
 
maurelima profile image
Marco Aurélio Silva Lima

That's so nice man! I wold like to ask... how can I export a custom hook using rollup? 🙏🏼

Collapse
 
dextersjab profile image
Dexter • Edited

Dope! Very well explained.

Collapse
 
hanifmhd profile image
Muhammad Hanif

Hi Alex,

Sorry if i ask you multiple times, have you built angular component? probably you can share it too. I'm curious if react component can be wrapped into angular

Collapse
 
dektaiimage profile image
Dektaiimage

How to Adding tailwindcss to Publish a React Component Library ?

Collapse
 
tonyftannous profile image
TonyFTannous

Hi Alex Eagleson.

good article. but i have a error when run the following: npm run rollup

Error: Could not resolve entry module (rollup.config.js).

please advice me???

Some comments have been hidden by the post's author - find out more