Bundling_a_react_library

Welcome to our comprehensive guide on bundling a React library into a reusable component library! In this journey, we’ll explore each step of the compilation process using real examples from the configuration files you’ve shared.

Understanding the Objective

Imagine you have a React library named @yugensys/react-widgets, and you want to package it into a component library for seamless integration into various projects. Goal here is to compile the source code, bundle it efficiently, and ensure compatibility with different module systems.

Configuration Files Overview

Let’s dive into the provided configuration files and understand how each section contributes to the compilation process.

webpack.config.js

Webpack is a powerful module bundler that orchestrates the compilation and bundling process. Let’s dissect the webpack.config.js file and understand its components using the provided example:
// webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

// Define webpack configuration
module.exports = {
  // Entry points for the library
  entry: {
    'App''./src/index.tsx',
    'App.min''./src/index.tsx'
  },
  // Output configuration
  output: {
    filename'[name].js',
    pathpath.resolve(__dirname'dist'),
    library'@yugensys/react-widets',
    libraryTarget'umd'// Universal Module Definition
    umdNamedDefinetrue
  },
  // Optimization settings
  optimization: {
    minimizetrue
  },
  // Source map generation for debugging
  devtool'source-map',
  // Resolve module file extensions
  resolve: {
    extensions: ['.tsx''.ts''.js''.scss''.css'],
    modules: ['node_modules']
  },
  // Define module rules for file handling
  module: {
    rules: [
      // TypeScript files handling
      {
        test/\.(ts|tsx)$/,
        use'ts-loader',
      },
      // CSS and SCSS files handling
      {
        test/\.(css|scss)$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',                // Convert CSS into CommonJS
          'sass-loader'                // Compile Sass to CSS
        ]
      },
      // File loader for other assets
      {
        test/\.(png|svg|jpg|jpeg|gif|json|ico)$/,
        use: ['file-loader?name=[name].[ext]']
      }
    ],
  },
  // Define webpack plugins
  plugins: [
    new CleanWebpackPlugin(),             // Clean output directory
    new HtmlWebpackPlugin({               // Generate HTML file
      template'./public/index.html',
    }),
    new MiniCssExtractPlugin({            // Extract CSS into separate files
      filename'[name].css',
    }),
  ],
  // Configuration for webpack-dev-server
  devServer: {
    static: {
      directorypath.join(__dirname'dist'),
    },
    compresstrue,
    port9000,
    opentrue,
  },
};

Here’s a brief note about each of the section, for a more detailed explanation please visit here:

  1. Entry Points Definition: The entry object defines the entry points for your library. Here, './src/index.tsx' serves as the primary entry point, while 'App.min.js' is designated for the minified version.
  2. Output Configuration: The output object determines where and how your bundled code will be generated. We specify the output directory, filenames, library name, and target (UMD) to ensure compatibility with various module systems.
  3. Optimization Settings: The optimization object allows us to specify optimization options. Enabling minimize: true triggers minification of the bundled code.
  4. Source Map Generation: The devtool option configures source map generation, aiding in debugging by mapping bundled code to its original source files.
  5. Module Rules: The module.rules array defines how different file types should be treated during bundling. Loaders like ts-loader, css-loader, sass-loader, and file-loader handle TypeScript, CSS/SCSS, and other asset files.
  6. Plugins: Webpack plugins enhance the bundling process. Here, plugins like CleanWebpackPlugin, HtmlWebpackPlugin, and MiniCssExtractPlugin provide functionalities such as cleaning the output directory, generating HTML files, and extracting CSS into separate files.
  7. Development Server Configuration: The devServer object configures webpack-dev-server settings, facilitating development with features like live reloading and hot module replacement.

 

tsconfig.json

The TypeScript compiler configuration (tsconfig.json) governs how TypeScript code is compiled into JavaScript. Let’s explore the purpose of each section in the provided tsconfig.json:

// tsconfig.json
{
  "compilerOptions": {
    // Compiler options
    "target""es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "module""CommonJS",
    "jsx""react-jsx"
  },
  "include": [
    "src"
  ],
  "exclude": [
    "dist",
    "**/*.test.ts",
    "**/webpack.config.js"
  ],
  "compilation": {
    "compiler": {
      "context""./src",
      "outputPath""./dist"
    }
  }
}
Let’s break down each section of the provided tsconfig.json file and understand how it contributes to the TypeScript compilation process and aids in bundling the TypeScript code.

1. "compilerOptions"

This section contains various settings for the TypeScript compiler (tsc) to govern how TypeScript code is compiled into JavaScript. Let’s examine each option:
  • "target": "es5": Specifies the ECMAScript version that the compiled JavaScript should be compatible with. In this case, the code is compiled to ES5, ensuring compatibility with older browsers and environments.
  • "lib": ["dom", "dom.iterable", "esnext"]: Defines the default library files to include when compiling TypeScript code. Here, we include dom and dom.iterable for DOM manipulation and iteration, along with esnext for modern ECMAScript features.
  • "module": "CommonJS": Specifies the module system to use for outputting JavaScript code. CommonJS modules are widely supported and commonly used in Node.js environments.
  • "jsx": "react-jsx": Defines how JSX (JavaScript XML) syntax should be handled. Here, react-jsx indicates that JSX should be transformed to React.createElement calls.

2. "include" and "exclude"

These sections determine which files should be included or excluded during the compilation process:
  • "include": ["src"]: Specifies the directory (or directories) where TypeScript files should be included for compilation. In this case, only files within the src directory will be compiled.
  • "exclude": ["dist", "**/*.test.ts", "**/webpack.config.js"]: Defines patterns for excluding specific files or directories from compilation. Here, files within the dist directory, any files ending with .test.ts, and the webpack.config.js file are excluded from compilation.

3. "compilation"

This section appears to be a custom configuration object named "compilation", but it’s not a standard TypeScript compiler option. It seems to define additional settings related to the compilation process:
  • "compiler": { "context": "./src", "outputPath": "./dist" }: This custom configuration seems to specify the compiler context and output path. The "context" property indicates the root directory for resolving entry files, while "outputPath" specifies the directory where compiled JavaScript files will be outputted. In this case, it suggests that the entry files are located within the src directory and the compiled output will be placed in the dist directory.
  The tsconfig.json file plays a crucial role in configuring the TypeScript compiler and guiding the compilation process. By defining compiler options, inclusion/exclusion patterns, and additional compilation settings, it ensures that TypeScript code is compiled correctly and bundled efficiently. In the context of bundling TypeScript code, this file ensures that the appropriate transformations and optimizations are applied during compilation, resulting in JavaScript code that is ready for bundling and deployment.

Finally, How to publish to a private Github NPM repository,

1. Create a Personal Access Token

First, we are going to a Create a Personal Access Token within Github to read and write your packages.

  1. You can do this by going to:
Github Account -> Settings -> Developer Settings -> Personal Access Tokens -> Generate New Token Select scopes: write:packages, read:packages

2. Click on Generate Token, and copy the token to the Clipboard.

3. With the token copied, in the same directory as your package.json file, create an .npmrc file and add the following line, replacing TOKEN with your personal access token.

//npm.pkg.github.com/:_authToken={TOKEN}
Here’s a screenshot of how your .npmrc file would look at this stage,

2. Create a Private Repository in Github

Create a Private repository in Github, just like you would normally and copy the URL. Mine would be:

git://github.com/yugensys/react-widgets

3. Add publish-config to package.json

To successfully publish, you need to make some small changes to your package.json.

  • So far, your package.json should have your folder’s name,
    "name": "react-widgets" .
  • In order to publish your package, you need to rename it to @owner/repo-name. In my case that’s
    "name": "@yugensys/react-widgets".
  • "files" : The folder with our generated library, i.e. the ./dist folder.
  • "publishConfig" : The registry where you want the package published, which is "registry": "https://npm.pkg.github.com".
  • "repository" : URL of the private Github repository we just created.
  • Lastly, if you have the following line in package.json, remove it. Setting "private": true in your package.json prevents it from being published at all. Don’t worry, your package will still be published as a private component library without it. 
 
At this stage, here’s how my package.json looks like,
{
  "name""@yugensys/react-widgets",
  "version""1.2.2",
  "main""dist/App.js",
  "private"false,
  "publishConfig": {
    "access""restricted"
  },
  "files": [
    "dist/",
    "lib/",
    "lib-esm/"
  ],
  "dependencies": {
    "@testing-library/jest-dom""^5.17.0",
    "@testing-library/react""^13.4.0",
    "@testing-library/user-event""^13.5.0",
    "@types/jest""^27.5.2",
    "@types/node""^16.18.96",
    "@types/react""^18.3.0",
    "@types/react-dom""^18.3.0",
    "react""^18.3.0",
    "react-dom""^18.3.0",
    "react-scripts""5.0.1",
    "typescript""^4.9.5",
    "web-vitals""^2.1.4",
    "@emotion/react""^11.11.4",
    "@emotion/styled""^11.11.5",
    "@material-ui/core""^4.12.4",
    "@mui/icons-material""^5.15.15",
    "@mui/lab""^5.0.0-alpha.170",
    "@mui/material""^5.15.15",
    "@mui/styled-engine""^5.15.14"
  },
  "scripts": {
    "start""webpack serve --mode development --open",
    "build""tsc && tsc -m es6 --outDir lib-esm && gulp && webpack --mode production",
    "test""react-scripts test",
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest",
      "plugin:storybook/recommended"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "peerDependencies": {
    "@emotion/react""^11.11.4",
    "@emotion/styled""^11.11.5",
    "@material-ui/core""^4.12.4",
    "@mui/icons-material""^5.15.15",
    "@mui/lab""^5.0.0-alpha.170",
    "@mui/material""^5.15.15",
    "@mui/styled-engine""^5.15.14"
  },
}

The package.json file serves as the backbone of a Node.js package, providing crucial metadata and configuration for bundling and publishing. Each section plays a vital role in ensuring that the package is properly bundled, dependencies are managed, and publishing is configured according to the desired specifications, especially when publishing to a private npm package repository.


4. Publish to Github registry

Finally, publish your library with the following command and it should be a success!

$ npm publish

Your package is now visible on Github on:

https://github.com/<owner>/<repo-name>/packages

Conclusion

Congratulations! You’ve gained a deeper understanding of the compilation process involved in bundling a React library into a component library. By dissecting the configuration files and exploring real examples, you’re now equipped to streamline the development and distribution of your React libraries. Armed with this knowledge, you can foster collaboration and code reuse within the community, making a significant impact in the world of web development. Happy coding!

About Author

Vaishakhi Panchmatia

As Tech Co-Founder at Yugensys, I’m passionate about fostering innovation and propelling technological progress. By harnessing the power of cutting-edge solutions, I lead our team in delivering transformative IT services and Outsourced Product Development. My expertise lies in leveraging technology to empower businesses and ensure their success within the dynamic digital landscape.

Looking to augment your software engineering team with a team dedicated to impactful solutions and continuous advancement, feel free to connect with me. Yugensys can be your trusted partner in navigating the ever-evolving technological landscape.

Subscribe to our newsletter.
Loading

Related Articles