This guide will walk you through the easiest process of building and releasing your NPM package, from start to finish, using a . microbundle Let’s talk a bit about it . I find it particularly effective for simple libraries because you don’t have to worry about configuration, allowing you to focus on developing your package. microbundle Here is a short list of its features: Built-in configs; all you have to do is add an “exports” field in package.json TypeScript support out of the box without tsconfig.json Multiple output formats (CJS, UMD, and ESM) Built-in Terser compression Basically, is built on top of . If you have more complex libraries to build than I will mention in this article, you might consider using a pure configuration. microbundle rollup.js rollup.js Initializing Your Package As an example, let’s create a simple library for summing two numbers, which will export only one function . sum Create a folder for the project, and run with default values to generate npm init package.json Create in folder index.ts src // src/index.ts export function sum(a: number, b: number) { return a + b; } Install microbundle npm i -D microbundle Update with the following values: package.json // package.json ... "type": "module", "source": "src/index.ts", // your source code "exports": { "types": "./dist/index.d.ts", // TypeScript declaration file "require": "./dist/index.cjs", // CommonJS entry point "default": "./dist/index.esm.js" // ES Module entry point }, "main": "./dist/index.cjs", // where to generate the CommonJS bundle "module": "./dist/index.esm.js", // where to generate the ESM bundle "unpkg": "./dist/index.umd.js", // where to generate the UMD bundle "types": "./dist/index.d.ts", // TypeScript declaration file for the package "scripts": { "build": "microbundle", // compiles "source" to "main", "module", "unpkg" "dev": "microbundle watch" // re-build when source files change } ... Run the build script npm run build The output should contain exactly the files that we declared in package.json And voilà, we made our first package. Let’s take a look at more complex scenarios. Adding React Into Your Library If you want to bring React to your library, you can still use it , but now, your build command should look like this: microbundle microbundle --jsx React.createElement --jsxFragment React.Fragment --jsxImportSource react --globals react/jsx-runtime=jsx Add the command to into script for future convenience: package.json build // package.json ... "scripts": { "build": "microbundle --jsx React.createElement --jsxFragment React.Fragment --jsxImportSource react --globals react/jsx-runtime=jsx" } ... Using Storybook for UI Components While building a UI library, you might need a sandbox where you can develop, visualize components, and provide demo components for your documentation. Here comes the . It’s a sandbox with its own convenient interface and bundler, where you can easily describe various states of your components. Each capture of your component state is called a "story." storybook This picture, taken from their documentation, shows what it looks like: Installing Storybook is quite simple; just run the command inside your library: npx storybook@latest init This command will install all required dependencies for Storybook, add scripts to run, and build Storybook into , create a folder with default configuration, and add some examples of stories to the folder . package.json .storybook src/stories Styling Library Integrating Into Your You can add styling in one of two ways: CSS file or CSS-in-JS. The CSS file allows easy customization but requires separate inclusion, whereas the CSS-in-JS simplifies styling but increases bundle size. CSS file Create a CSS file in the src directory, and import it into the root react component file: // src/index.tsx import './styles.css'; export const MySuperComponent = () => { return ( <h1 className="title">Hi there!</h1> ) }; So, let’s run the build command again. npm run build And your imported file will be created in the folder: styles.css dist Great! We have obtained a CSS file with the necessary styles. However, there is a slight inconvenience with this solution. The CSS file needs to be added separately after the package is installed. Therefore, users of your library will need to use a CSS loader (e.g., a CSS-loader for the webpack) to handle your CSS file, and their usage will look like this: import { MySuperComponent } from 'my-super-library'; import 'my-super-library/dist/styles.css'; export const App = () => { return ( <MySuperComponent /> ); } CSS-in-JS You can use libraries like for this purpose. And it will look like this: styled-components import styled from 'styled-components'; const Title = styled.h1` font-size: 30px; font-weight: bold; `; export const MySuperComponent = () => { return ( <Title>Hi there!</Title> ) }; With this solution, users won’t need to import a CSS file and add a special loader for their project. After installing the library, the component will come with its own styling. However, this will increase the bundle size and make it more difficult for users to customize elements using CSS selectors. Choose the option that suits you best. I prefer to use the CSS file because it allows users to customize any element with CSS selectors, doesn’t affect the bundle size, and works faster. Developing a Detailed File README.md A file provides information about your library, how to install it, its basic usage, and the features it has. This is often the first file that developers read when they encounter your repository or NPM package, so it should be concise and informative. README.md I like to create a structure in the following order: Title Super short package description Fancy statistic badges ( ) shields.io If your library is a UI component, include a screenshot or provide a demo link on CodeSandbox Features list Installation guide Code fragments with usage Options and props that your library accepts for configuration You can refer to examples of files from my packages, such as and , for inspiration. README.md dot-path-value react-nested-dropdown Navigating Dependency Management This is an important part because if you do it wrong, users may face version conflicts or other problems, and they will have to remove your library. So, let's take a look at the main differences between dependency types. “devDependencies” are only for development and will not be included in your bundle. For example, if you have the package installed, which users don’t need to use, keep it in devDependencies, and it won’t appear in the bundle. microbundle "dependencies" will be installed along with the package. Include dependencies that your package needs to work in users’ projects. Note that some dependencies, such as “react,” might already be installed in the user’s project. Having a duplicate “react” in your package could increase the bundle size. This is where “peerDependencies” come in handy. “peerDependencies” are dependencies that your package uses but won’t include in your bundle. Your package will utilize the version of the dependency that the user has in their project. Basically, we should specify a peer dependency if we are creating a library for its ecosystem. For example, if you are creating a React component, set React as a peer dependency. If developing a React component with a calendar, add React and a date calculation library (e.g., date-fns) as peerDependencies. If the user doesn’t have the specified peer dependency in their project, the command will display a warning, but it will not automatically install the dependency. npm install Just a small example of how it looks: // package.json ... "dependencies": { // libraries which will be installed along with your library "clsx": "^1.2.1" // just library for className combining }, "peerDependencies": { // user should have these packages installed "react": "^16.8.0 || ^17.0.0 || ^18.0.0" // user should have react 16.8+ version }, "devDependencies": { // won't be in user's bundle, these libraries just for your developing needs "@types/react": "^18.2.33", "react": "^18.2.0", // in case if we are using storybook, we need actual react library to render our components there "microbundle": "^0.15.1", }, ... Using GitHub for Your Package If you are publishing an NPM package, it means it will be publicly accessible (if you have a free account). To gather feedback from users, you can create a GitHub repository for your original code. People can create issues and communicate with you about your package there. You can also describe your releases and get some stars! You can certainly skip this step, but it is an integral part of the developer culture and can be a valuable contribution to open-source. Publishing and Maintaining the Package Before you can publish your package, it's essential to ensure that your file is properly configured. Here are some important steps to follow: package.json Name and try to describe the core functionality of your library. For example: "name": "react-color-picker" Add GitHub repository information (if it exists): ... "homepage": "https://github.com/{your_repository}", "repository": { "type": "git", "url": "https://github.com/{your_repository}" }, "bugs": { "url": "https://github.com/{your_repository}/issues" }, ... Configure the : files ... "files": [ "dist", ], ... You should specify the files that will be included in , when your library is installed. Usually, including the folder is sufficient. node_modules dist Add : keywords Keywords are an array of strings that describe your package and are used by NPM for searches and recommendations. Choose words relevant to your package that you anticipate people will use when searching. Let’s create keywords for our library: sum ... "keywords": ["typescript", "math", "sum", "numbers", "arithmetic", "calculator", "calculation"] ... It’s important to specify your technologies because users often search for terms like “typescript library for math” or “react calendar picker.” Create an account if you haven’t already, and run in your terminal; follow the prompts to authenticate your account. By default, the version of your package will be ; you can check it in the file. I recommend changing it to . NPM npm login 1.0.0 package.json 0.0.1 Run , and you're done! To update the version in the future, use the command to increment the version, and then publish the updated package with . npm publish npm version patch npm publish Conclusion As you can see, creating your own library is very easy! Essentially, this is all you need for creating and maintaining the package. If you struggle with limiting your library with , I recommend using with a more specific configuration. microbundle rollup.js Creating NPM packages and contributing to open-source is a rewarding and valuable experience for developers of all skill levels. It allows you to share your code with the community, gain a lot of experience, and build a portfolio of your work.