Creating a React Component Library with TypeScript, Storybook & Rollup

Search for a command to run...

Thanks for the guide Cristiana! I just went through this dance myself and here are some additional things I found interesting:
npm pack to test out what's getting placed in your published package. Also, it lets you npm i path/to/tarbal-0-0-0.tgz to verify. Much more reliable then pure npm linking on both sides. typescript({
declaration: true,
}),
and in my tsconfig
"noEmit": false,
"emitDeclarationOnly": true,
and then use tsc to build the types with:
"build:types": "tsc -p tsconfig.json && tsc -p tsconfig-cjs.json",
where my tsconfig-cjs.json extended the main one just changing the module to CommonJS and outDir to dist/cjs. It seems dist is most popular default e.g. for create-react-app aka react-scripts it seems to "want" stuff in dist (I had put stuff in lib before).
It's probably worth experimenting with the postcss options:
postcss({
minimize: true,
sourceMap: true,
extract: true,
}),
I wanted my css minimized and not inlined. I thought I'd need to add module: true but there's a default in postcss that if you already name your files foo.module.css it'll treat it as a CSS module anyway (of course this is only for folks who care about that).
All this was done for my AgnosticUI React package (but similar for Vue and Svelte packages too)
I definitely took a lot of tweaking and I referenced many other blog articles and docs before getting it to my liking. Thanks again for yours Cristiana!
Christiana, this is such a crisp and clean guide. Thank you!
One thing I've noticed. It seems you presume that the rollup is installed globally, as you have not included it in the dependencies.
If rollup is not present globally, the npm run build doesn't work.
If this helps somebody:
Option 1 - npm install -g rollup
Option 2 - npm install --save-dev rollup
Happy to hear!
I haven't used Storybook before so, I definitely learned a lot from this article.
Amazing article, Cristiana Man. Thanks for sharing.
Thanks for the support! I am really glad to hear you learned something from it!
Thanks! I am so happy this article was helpful! Good luck with the popups library!
This blog post is fourteenth in the Advent of Code 2021 series and shows a JavaScript-based solution to the problem described in Day 14. This challenge was about optimizing an algorithm that generates exponentially larger and larger strings. I found ...

This blog post is thirteenth in the Advent of Code 2021 series and shows a JavaScript-based solution to the problem described in Day 13. All solutions are in this GitHub repository. Solving Part One Given a list of x, y coordinates representing dots ...

This blog post is twelfth in the Advent of Code 2021 series and shows a JavaScript-based solution to the problem described on Day 12. All solutions are in this GitHub repository. Solving Part One Given a set of connections between underground caves, ...

This blog post is eleventh in the Advent of Code 2021 series and shows a JavaScript-based solution to the problem described in Day 11. This day had many similarities to Day 9, and I solved the challenge using similar methods. All solutions are in thi...

This blog post is the tenth in the Advent of Code 2021 series and shows a JavaScript-based solution to the problem described in Day 10. All solutions are in this GitHub repository. Solving Part One Given a set of lines with opening and closing bracke...

A shared UI component library is fundamental to the frontend architecture of a growing company where you have teams maintaining several projects that use the same visual elements. The UI component library ensures visual consistency between projects owned by different teams and improves productivity since you can update components, fix visual issues, and solve similar problems in a single place.
If you have a single project, you don't need a separate library for your components as you can define them directly in your project.
This post shows how to set up and get started using
You can find the resulting project on GitHub.
To get started, we can create a new project called ui using Create React App:
npx create-react-app ui --template typescript
This generates boilerplate code that allows us to create React components and test them using React Testing Library.
You can remove start, build and eject scripts from package.json because we will use Storybook to start our project and Rollup to bundle a production-ready package.
"scripts": {
"test": "react-scripts test"
},
You can also remove App.tsx, App.css, App.test.tsx, index.css, index.tsx, logo.svg, reportWebVitals.ts since we won't use them.
Storybook is a wonderful tool for developing, showcasing and documenting UI components in isolation. Storybook works with any component-based library in JavaScript such as React, Vue, Angular, and more.
To install Storybook in our React application, run this command:
npx sb init
You should now be able to run Storybook locally by running npm run storybook or if you prefer yarn storybook.
Here is a preview of the Storybook application:


stories folderBy default, the npx sb init command creates a stories folder inside src with example components and documentation pages. While you can safely remove this folder, I recommend exploring it first!
Introduction.stories.mdx file contains the documentation used to generate the Introduction to Storybook page in the image preview above. The file is written using MDX format, which is a combination of Markdown and JSX, so you can write components directly into the documentation. The links in this file are also worth exploring! Button.tsx and Button.stories.tsx are great examples of how you can define a component and a corresponding page in Storybook. The Button story documents available props, code usage, and showcases component variations. Through the Controls, you can experiment with toggling and customizing props.
.storybook folderContains files for customizing Storybook:
main.js defines the file pattern used by Storybook to determine what to include in the showcase application. By default, Storybook uses files containing .stories in their name. "stories": [
"../src/**/*.stories.mdx",
"../src/**/*.stories.@(js|jsx|ts|tsx)"
]
Addons for the Storybook application are also defined in main.js. "addons": [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/preset-create-react-app"
]
preview.js configures how actions and controls will show up depending on the prop's name. By default, props starting with on such as onClick, onChange, onSubmit are automatically interpreted by Storybook as actions, so when triggered, they get logged inside Storybook's Actions addon. Besides, props suffixed with background and color will show a color picker control, whereas props suffixed with Date display a date picker control.
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
}

package.json fileThis file is automatically updated with Storybook development dependencies, custom eslint configuration for stories, and the following scripts:
"scripts": {
"test": "react-scripts test",
"storybook": "start-storybook -p 6006 -s public",
"build-storybook": "build-storybook -s public"
},
npm run storybook starts the Storybook application locallynpm run build-storybook builds the Storybook application ready for deploymentRollup needs an entry point to generate the bundle. Let's create index.ts in the src file that exports each of our components generated by Storybook:
export * from "./stories/Button"
export * from "./stories/Header"
export * from "./stories/Page"
Rollup is a good bundling tool choice if we want to package the React component library and reuse it in other projects.
In Webpack and Rollup: the same but different Rich Harris recommends using Webpack for applications and Rollup for libraries.
First, you need to install dependencies either using npm
npm install @rollup/plugin-commonjs @rollup/plugin-node-resolve rollup-plugin-peer-deps-external @rollup/plugin-typescript postcss rollup-plugin-postcss --save-dev
or with yarn
yarn add rollup @rollup/plugin-commonjs @rollup/plugin-node-resolve rollup-plugin-peer-deps-external @rollup/plugin-typescript postcss rollup-plugin-postcss -D
Let's understand these dependencies:
rollup gives the command-line interface (CLI) to bundle the library@rollup/plugin-commonjs converts CommonJS modules (potentially used in node_modules) to ES6, which is what Rollup understands. If you don't know what CommonJS modules are, this article explains module systems in JavaScript: What the heck are CJS AMD UMD and ESM in JavaScript?@rollup/plugin-node-resolve helps Rollup understand how to import dependencies in node_modules.@rollup/plugin-typescript transpiles TypeScript files to JavaScript.rollup-plugin-peer-deps-external prevents adding peer dependencies to the bundle because the consumer of the library is expected to have them. So we also get a smaller bundle size.rollup-plugin-postcss integrates with PostCss tool for bundling styles. You can configure this tool with CSS modules, Less, Sass, depending on your preference.Next, we need to update package.json.
According to the Rollup Wiki, libraries should be distributed using CommonJS and ES6. We specify the output file paths using main and module properties. We also use these properties in the Rollup configuration file.
Then, add a build script that uses the rollup command-line interface with the -c argument. This means that Rollup will look for a configuration file named rollup.config.js to bundle the component library.
Add react and react-dom to peerDependencies and devDependencies (we still need them for Storybook) and remove these from dependencies. This is needed by rollup-plugin-peer-deps-external plugin to prevent distributing and bundling dependencies that the consumer library already has.
{
...
"main": "./build/index.js",
"module": "./build/index.es.js",
"scripts": {
...
"build": "rollup -c"
},
"peerDependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"devDependencies": {
...
"react": "^17.0.2",
"react-dom": "^17.0.2"
}
}
Finally, the rollup.config.js file has the following contents:
import commonjs from "@rollup/plugin-commonjs";
import resolve from "@rollup/plugin-node-resolve";
import peerDepsExternal from "rollup-plugin-peer-deps-external";
import typescript from "@rollup/plugin-typescript";
import postcss from "rollup-plugin-postcss";
import packageJson from "./package.json";
// eslint-disable-next-line import/no-anonymous-default-export
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(),
postcss()
]
};
We created a React component library powered by Create React App. We installed Storybook to create a documentation page and enable developing components in isolation. Finally, we configured Rollup for bundled the library.
While this is one way of getting started, an alternative approach could be based on Create React Library. Let me know if you would like to see an article about that.
I hope you found this interesting. Thanks for reading!