{"id":2226,"date":"2022-01-25T17:44:43","date_gmt":"2022-01-25T17:44:43","guid":{"rendered":"https:\/\/lvboard.infostore.in.ua\/?p=2226"},"modified":"2022-01-25T17:44:43","modified_gmt":"2022-01-25T17:44:43","slug":"how-to-create-your-own-react-component-library","status":"publish","type":"post","link":"https:\/\/lvboard.infostore.in.ua\/?p=2226","title":{"rendered":"How to Create Your Own React Component Library"},"content":{"rendered":"\n<p id=\"2d7a\">So we\u2019ve already created and published packages for both\u00a0<a rel=\"noreferrer noopener\" target=\"_blank\" href=\"https:\/\/javascript.plainenglish.io\/create-and-publish-your-own-npm-module-216bffe82dc5\">Node.js<\/a>\u00a0and\u00a0<a rel=\"noreferrer noopener\" target=\"_blank\" href=\"https:\/\/javascript.plainenglish.io\/create-your-own-angular-component-library-e5f062b902a8\">Angular<\/a>\u00a0in this series. Now it\u2019s time to do it for React. <\/p>\n\n\n\n<!--more-->\n\n\n\n<p id=\"2d7a\">In the case of Angular, we had an option to natively create a \u201c<strong>library<\/strong>\u201d project and then test it inside an \u201c<strong>application<\/strong>\u201d project. We were able to make changes to the library and see those changes inside the application just like a typical single-page web application. In the case of React, to test our components we\u2019ll have to go for a third-party library. The library that we\u2019ll be working with is called Storybook.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote\"><p><a href=\"https:\/\/youtu.be\/d8oztfRBGrI\" rel=\"noreferrer noopener\" target=\"_blank\">There\u2019s also a video version of this available on YouTube.<\/a><\/p><\/blockquote>\n\n\n\n<h2 id=\"36b5\">Why storybook<\/h2>\n\n\n\n<p id=\"3cb7\">Storybook provides you with an environment where you can create your components in isolation, with change detection just like you would in the case of a typical single-page application. It also provides a clean user interface that lets you view different variants of the component that you\u2019re creating.<\/p>\n\n\n\n<h2 id=\"e0bb\">Project setup<\/h2>\n\n\n\n<p id=\"6bab\">So let\u2019s quickly set up our project. Since we\u2019ll be creating a React component library and not a React application, we won\u2019t be using&nbsp;<strong>create-react-app<\/strong>. We\u2019ll instead build the skeleton from scratch. So open up an empty folder inside VSCode and type&nbsp;<code>npm init -y<\/code>&nbsp;to initialize a new package. You\u2019ll find a package.json file inside the folder. Now you need to install the following list of dependencies.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">npm install --save-dev react react-dom typescript @types\/react<\/pre>\n\n\n\n<p id=\"5a55\">We need to install&nbsp;<strong>react<\/strong>&nbsp;and&nbsp;<strong>react-dom<\/strong>&nbsp;to create our component\u2019s UI and interact with the DOM. I\u2019m also adding&nbsp;<strong>typescript<\/strong>&nbsp;and&nbsp;<strong>type declarations<\/strong>&nbsp;for React to use static type checking. You can skip typescript if you want to.<\/p>\n\n\n\n<p id=\"0791\">Since we\u2019re building a React component, we also need to add react and react-DOM as&nbsp;<strong>peer dependencies<\/strong>&nbsp;in our package.json file. This will basically ensure that the consumer of this package is going to be a React application. So inside your package.json file, add a peer dependencies object.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img src=\"https:\/\/miro.medium.com\/max\/362\/1*fFEExF0dTxYBvSvmF9urAg.png\" alt=\"\"\/><figcaption>Peer dependencies inside package.json<\/figcaption><\/figure>\n\n\n\n<p id=\"2e85\">Once this is done, we\u2019ll initialize typescript by typing in&nbsp;<code>npx tsc --init<\/code><\/p>\n\n\n\n<p id=\"2d97\">This should create a tsconfig.json file inside your project with some default options. You can replace these options with the following<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">{<br>   \"compilerOptions\": {<br>      \"jsx\": \"react\",<br>      \"target\": \"es2016\",<br>      \"outDir\": \"dist\",<br>      \"allowJs\": true,<br>      \"skipLibCheck\": true,<br>      \"strict\": true,<br>      \"forceConsistentCasingInFileNames\": true,<br>      \"module\": \"ES2020\",<br>      \"allowSyntheticDefaultImports\": true,<br>      \"moduleResolution\": \"Node\",<br>      \"declaration\": true,<br>      \"declarationDir\": \"dist\"<br>   },<br>   \"include\": [<br>      \"stories\"<br>   ]<br>}<\/pre>\n\n\n\n<ol><li><strong>jsx:&nbsp;<\/strong>To work with JSX in our React component<\/li><li><strong>target<\/strong>: Changes which JS features are down leveled and which are left intact. For example, an arrow function&nbsp;<code>() =&gt; this<\/code>&nbsp;will be turned into an equivalent&nbsp;<code>function<\/code>&nbsp;expression if&nbsp;<code>target<\/code>&nbsp;is ES5 or lower.<\/li><li><strong>outDir<\/strong>: Files will be emitted into this directory after compilation.<\/li><li><strong>allowJs<\/strong>: Allows JS files to be imported inside our project.<\/li><li><strong>skipLibCheck<\/strong>: Skip type checking of declaration files.<\/li><li><strong>strict<\/strong>: A more robust type-checking which results in better overall correctness.<\/li><li><strong>forceConsistentCasingInFileNames:&nbsp;<\/strong>Will throw errors if there are inconsistencies in naming based on the file system\u2019s case sensitivity<\/li><li><strong>module<\/strong>: Sets the type of the module (es6, es2020, commonjs, umd)<\/li><li><strong>allowSyntheticDefaultImports<\/strong>: This basically lets you import your module in a more convenient way, for instance you can use&nbsp;<code>import React from 'react'<\/code>&nbsp;instead of&nbsp;<code>import * as React from 'react';<\/code>&nbsp;by setting this option to true.<\/li><li><strong>moduleResolution<\/strong>: You can set the module resolution strategy using this, which in our case is going to be Node.<\/li><li><strong>declaration<\/strong>: Generate&nbsp;<code>.d.ts<\/code>&nbsp;files for every TypeScript or JavaScript file inside your project.<\/li><li><strong>declarationDir<\/strong>: directory inside which the declaration files will be stored<\/li><li><strong>include<\/strong>: Files to be included for compilation<\/li><\/ol>\n\n\n\n<p id=\"b53a\">You can also add an&nbsp;<strong>exclude<\/strong>&nbsp;option which takes in an array of files\/folders if you want them to not be included in the compilation process.<\/p>\n\n\n\n<p id=\"707a\">Now we can initialize Storybook by typing in&nbsp;<code>npx sb init<\/code>&nbsp;.<\/p>\n\n\n\n<p id=\"33ef\">This command would not work inside an empty project. It looks inside our project dependencies and makes a calculated guess about the type of our project (in this case, React).<\/p>\n\n\n\n<p id=\"6f2d\">Once it\u2019s done, inside your package.json file, you\u2019ll find a few extra dependencies, and some new scripts to run\/build Storybook. There will be two new folders inside your project. The&nbsp;<strong>.storybook<\/strong>&nbsp;folder will have your default storybook configuration and the&nbsp;<strong>stories<\/strong>&nbsp;folder will have some dummy stories that you can test.<\/p>\n\n\n\n<p id=\"05c4\">Now inside your terminal, type in&nbsp;<code>npm run storybook<\/code>&nbsp;.(This script was added when you initialized Storybook). Storybook\u2019s interactive UI will open up in your browser with all your dummy components.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img src=\"https:\/\/miro.medium.com\/max\/700\/1*JtjZxuqsWYSmfhJ3RZByLA.png\" alt=\"\"\/><figcaption>Storybook playground<\/figcaption><\/figure>\n\n\n\n<p id=\"d9c0\">We only care about the button component here, so you can go ahead and delete everything else inside your stories folder.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img src=\"https:\/\/miro.medium.com\/max\/225\/1*4F7ua8rdZBcGrYHaXxNvMw.png\" alt=\"\"\/><figcaption>Keep only button component files.<\/figcaption><\/figure>\n\n\n\n<p id=\"98b0\">A story is the rendered state of your React component. So for each React component, we\u2019ll have a corresponding story which gets rendered on the browser whenever you run Storybook. You can reactively make changes to these stories and play around with their controls to see how your button is going to look like inside an application. Once you have completely tested your component, you can then push the component to npm. These story files are not going to be included in the production build, so you don&#8217;t have to worry about the bundle size.<\/p>\n\n\n\n<p id=\"6e9e\">So we have our&nbsp;<strong>Button.tsx<\/strong>&nbsp;file which is a React component.https:\/\/javascript.plainenglish.io\/media\/cbb97a1967c74add13cd5512d0bf219d<\/p>\n\n\n\n<p id=\"3b10\">This react component is a typical button with a set of props. This button has a corresponding story file called&nbsp;<strong>Button.stories.tsx<\/strong>.https:\/\/javascript.plainenglish.io\/media\/7996bb2e220e90eb1c410f56bf67e118<\/p>\n\n\n\n<p id=\"4a7b\">Inside this story, you first import your Button react component. After that, you create a default export for your button with some configurations (title, component). The&nbsp;<strong>args<\/strong>&nbsp;or&nbsp;<strong>arguments<\/strong>&nbsp;option defines how a component should render by providing a default set of values for our props.<\/p>\n\n\n\n<p id=\"eccc\">Below this default export, we have a series of named exports which are just different variants of this default button (You\u2019ll need these to render your buttons on Storybook).<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">const Template: ComponentStory&lt;typeof Button&gt; = (args) =&gt; &lt;Button {\u2026args} \/&gt;;<\/pre>\n\n\n\n<p id=\"b280\">is just a fancy way of creating a template that can be used for each of our variants. So any args that you provide inside these variants will override the default ones.<\/p>\n\n\n\n<p id=\"4b58\">Now if you go back to the Storybook playground on your browser, you\u2019ll see the buttons rendered and a controls section at the bottom. This controls section lets you customize your component by tweaking around with the prop values.<\/p>\n\n\n\n<p id=\"eec2\">You can see how easy it becomes to test components in isolation using Storybook. Now that we have created and tested our React component, it\u2019s time to publish it.<\/p>\n\n\n\n<h2 id=\"d79e\">Bundling your component<\/h2>\n\n\n\n<p id=\"5fd3\">There are several tools that one could use to compile and bundle their project. Webpack is a fan favorite but is most commonly used for applications and not component libraries. Rollup on the other hand is perfect for our use case, so we\u2019ll be using Rollup. Install these libraries and plugins.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">npm i --save-dev <strong>rollup<\/strong> <strong>@rollup\/plugin-node-resolve<\/strong> <strong>@rollup\/plugin-babel<\/strong> <strong>rollup-plugin-uglify<\/strong> <strong>rollup-plugin-postcss<\/strong> <strong>rollup-plugin-typescript2<\/strong> <strong>rollup-plugin-peer-deps-external<\/strong><\/pre>\n\n\n\n<p id=\"4a6b\"><a href=\"https:\/\/www.npmjs.com\/package\/rollup\" rel=\"noreferrer noopener\" target=\"_blank\"><strong>rollup<\/strong><\/a><strong>&nbsp;<\/strong>\u2014 Main module bundler library.<\/p>\n\n\n\n<p id=\"72aa\"><a href=\"https:\/\/www.npmjs.com\/package\/@rollup\/plugin-node-resolve\" rel=\"noreferrer noopener\" target=\"_blank\"><strong>@rollup\/plugin-node-resolve<\/strong><\/a>&nbsp;\u2014 Used to locate third-party modules used inside our project (we don\u2019t have any third-party modules in our app currently, but this might change in the future).<\/p>\n\n\n\n<p id=\"09ab\"><a href=\"https:\/\/www.npmjs.com\/package\/@rollup\/plugin-babel\" rel=\"noreferrer noopener\" target=\"_blank\"><strong>@rollup\/plugin-babel<\/strong><\/a>\u2014 To integrate with babel.<\/p>\n\n\n\n<p id=\"bb86\"><a href=\"https:\/\/www.npmjs.com\/package\/rollup-plugin-uglify\" rel=\"noreferrer noopener\" target=\"_blank\"><strong>rollup-plugin-uglify<\/strong><\/a>&nbsp;\u2014 To minify your final bundle.<\/p>\n\n\n\n<p id=\"fb61\"><a href=\"https:\/\/www.npmjs.com\/package\/rollup-plugin-postcss\" rel=\"noreferrer noopener\" target=\"_blank\"><strong>rollup-plugin-postcss&nbsp;<\/strong><\/a>\u2014 Includes the CSS that we created as separate files in our bundle. It does this by generating minified CSS from the *.css files and includes them via the&nbsp;<code>&lt;head&gt;<\/code>&nbsp;tag wherever used in our components.<\/p>\n\n\n\n<p id=\"f322\"><a href=\"https:\/\/www.npmjs.com\/package\/rollup-plugin-typescript2\" rel=\"noreferrer noopener\" target=\"_blank\"><strong>rollup-plugin-typescript2<\/strong>&nbsp;<\/a>\u2014 This plugin compiles the TypeScript code to JavaScript for our final bundle and generates the type declarations for the&nbsp;<code>types<\/code>&nbsp;key in&nbsp;<code>package.json<\/code>.<\/p>\n\n\n\n<p id=\"c10d\"><a href=\"https:\/\/www.npmjs.com\/package\/rollup-plugin-peer-deps-external\" rel=\"noreferrer noopener\" target=\"_blank\"><strong>rollup-plugin-peer-deps-external<\/strong><\/a><strong>&nbsp;<\/strong>\u2014 This plugin will externalize our&nbsp;<code>peerDependencies<\/code>&nbsp;(react and react-dom in our case) in the final bundle as these will be provided by the consumer application.<\/p>\n\n\n\n<p id=\"2a46\">This is what\u2019s great about rollup. You have a host of plugins that you can add to your project to do specific tasks without doing them yourself. You can check out all the plugins available&nbsp;<a href=\"https:\/\/github.com\/rollup\/awesome\" rel=\"noreferrer noopener\" target=\"_blank\">on their GitHub repo.<\/a><\/p>\n\n\n\n<p id=\"d45f\">After installation, inside your root directory, create a config file called rollup.config.js and copy the following code.https:\/\/javascript.plainenglish.io\/media\/bbf7adef0188d5feb8506a157afa5627<\/p>\n\n\n\n<p id=\"79cc\">We simply export an object with a set of properties. There will be an&nbsp;<strong>input&nbsp;<\/strong>and&nbsp;<strong>output&nbsp;<\/strong>option. The input option is the entry point of your application. So inside our root directory, you\u2019ll need to create this entry point file (index.js) which will basically export all the components from our component library.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">import { Button } from '.\/stories\/Button'export { Button }<\/pre>\n\n\n\n<p id=\"5f6d\">The output key in your rollup config file is going to be the production bundle that gets generated after compilation. So when you build your component, after completion you\u2019ll find a dist folder inside which there\u2019s gonna be an index.js, which essentially is our component library.<\/p>\n\n\n\n<p id=\"9842\">In the end, we pass all the plugins that we installed in the plugins array.<\/p>\n\n\n\n<p id=\"df2c\">Once this is done, you need to go to your package.json file. Inside the scripts object, add the following script&nbsp;<code>buildLib: \"rollup -c\u201d<\/code>&nbsp;. This command will look for a config file inside your project and run the compilation and bundling process based on the options provided inside the file. You also need to change the main key inside your package.json file to \u201cdist\/index.js\u201d if you haven&#8217;t done that already.<\/p>\n\n\n\n<p id=\"3b53\">Now inside your terminal, run the&nbsp;<strong>buildLib&nbsp;<\/strong>command&nbsp;<code>npm run buildLib<\/code>. Once it\u2019s done, you\u2019ll now have a dist folder inside your project which will have an index.js file. You\u2019ll be publishing this file to the npm store in the next and final step.<\/p>\n\n\n\n<h2 id=\"88c3\">Publishing your component to npm<\/h2>\n\n\n\n<p id=\"9279\">Before publishing your component to the store, there are a couple of steps that you\u2019ll need to follow. All these steps have already been covered in the previous blog post which you can find&nbsp;<a rel=\"noreferrer noopener\" target=\"_blank\" href=\"https:\/\/javascript.plainenglish.io\/create-and-publish-your-own-npm-module-216bffe82dc5\">over here<\/a>&nbsp;or this&nbsp;<a href=\"https:\/\/www.youtube.com\/watch?v=f0h-0d6Y_94&amp;ab_channel=AkileshRao\" rel=\"noreferrer noopener\" target=\"_blank\">video over here<\/a>.<\/p>\n\n\n\n<ol><li><strong>Create and login into your npm account.<\/strong><br><a href=\"https:\/\/www.npmjs.com\/signup\" rel=\"noreferrer noopener\" target=\"_blank\">You\u2019ll need to create an npm account first<\/a>. After registration, you\u2019ll need to log in to your npm account inside VSCode. So inside your terminal, type \u201c<strong>npm login<\/strong>\u201d. It will ask for the same set of credentials that you filled in when registering your account. Once this is done, you can move on to the next step.<\/li><li><strong>Add a scope to the package.<br><\/strong>NPM will not allow two packages to have the same or even a similar name. So you\u2019ll need to add a scope to your package which will make your package name unique. The scope, in this case, is going to be your username. So inside the package.json file, the name key is currently set to&nbsp;<strong>react-component-library.&nbsp;<\/strong>Change it to @\u201cyou-user-name\u201d\/<strong>react-component-library<\/strong>. So since my npm username is \u201cakileshrao19\u201d, my package name will be @akileshrao19\/react-component-library. Again, the reason behind doing this is explained in detail in the&nbsp;<a href=\"https:\/\/www.youtube.com\/watch?v=f0h-0d6Y_94&amp;ab_channel=AkileshRao\" rel=\"noreferrer noopener\" target=\"_blank\">previous blog post<\/a>.<\/li><\/ol>\n\n\n\n<p id=\"ed21\">Once you\u2019re done with these steps, you can finally publish it to the store&nbsp;<code>npm publish --access=public<\/code><\/p>\n\n\n\n<p id=\"51c5\">Packages with a scope in their name are private by default and to publish private packages you need a paid monthly subscription. So instead, we\u2019ll publish it publicly, by adding the \u201c<strong>access=public<\/strong>\u201d flag.<\/p>\n\n\n\n<p id=\"1883\">If you followed along and did everything correctly, you\u2019ll get a successful response which may look something like this<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">+@YOUR-USERNAME\/react-component-library@1.0.0<\/pre>\n\n\n\n<p id=\"fed2\">You can go to npm\u2019s website and log in. Inside your packages section, you\u2019ll find your newly created React component. You can test out this component by importing it inside a React application as you\u2019d normally do in the case of third-party packages.<\/p>\n\n\n\n<h2 id=\"49a4\">Conclusion<\/h2>\n\n\n\n<p id=\"14f7\">With that, you\u2019ve successfully created, tested, and published our React component to the npm store.&nbsp;<a href=\"https:\/\/github.com\/AkileshRao\/react-component-library\" rel=\"noreferrer noopener\" target=\"_blank\">You can access the codebase for this project from its Github repo.<\/a><\/p>\n\n\n\n<p id=\"4096\"><a href=\"https:\/\/youtu.be\/d8oztfRBGrI\" rel=\"noreferrer noopener\" target=\"_blank\">A video version for this tutorial is also available on Youtube.<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>So we\u2019ve already created and published packages for both\u00a0Node.js\u00a0and\u00a0Angular\u00a0in this series. Now it\u2019s time to do it for React.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[30],"tags":[65],"_links":{"self":[{"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=\/wp\/v2\/posts\/2226"}],"collection":[{"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2226"}],"version-history":[{"count":1,"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=\/wp\/v2\/posts\/2226\/revisions"}],"predecessor-version":[{"id":2227,"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=\/wp\/v2\/posts\/2226\/revisions\/2227"}],"wp:attachment":[{"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2226"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2226"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2226"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}