Asset Management for a React Library using webpack

February 2, 2024
 
How to create and use a webpack React library with image assets in components and CSS
In this article we will be creating a React component library that contains images using webpack. The library will contain reusable components (containing images) that can be installed into other React applications from either the npm registry or directly from git.

I emphasize that this is a React library which is different from an application, as your distribution code will be used as a node module and has different behavior from an application.

In this article we'll be demonstrating the usage of the asset management within webpack 5 to support the distribution of images as assets to your library. Here is what the resulting React application will look like that is utilizing the React library components which contains images:

NOTICE: The library component being used contains images in both the resulting HTML and also in the CSS to demonstrate how the assets are being loaded for both scenarios.

Setup package.json

Our starting point will be to setup our project to utilize npm by creating the package.json file at the root of your source code.

Create a new directory for your library
mkdir cg-react-webpack-asset-lib
cd cg-react-webpack-asset-lib

Create package.json
npm init

Install Babel as your JavaScript compiler
npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/preset-react babel-loader

Install webpack
npm install --save-dev webpack webpack-cli

Install React
npm install --save-dev react

Install CSS Loader
npm install --save-dev style-loader css-loader

Note: we installed our dependencies as a development dependency (—save-dev) so that they are not included in the build.

Create Library Components

Now that your project is setup to support npm we can create our components. You can follow along to build the project yourself, which is recommended, or you can download here:
https://CodeGorilla@bitbucket.org/CodeGorilla/cg-react-webpack-asset-lib.git

The project will have the following structure:


Create src/components folder

Create code-gorilla-package.css in src/components folder
.code-gorilla-div {
    color: #007bff;;
    background-image: url("./code-gorilla-bg.png");
    font-family: Herculanum;
    font-size: 16px;
    border: 2px solid #adff2f;
    border-radius: 5px;
    padding: 10px;
    margin: 5px;
    text-align: center;
  }

Create CodeGorillaArticle.js in src/components folder.
import React from 'react';
import './code-gorilla-package.css';
import Gorilla from './code-gorilla-logo.png';

const CodeGorillaArticle = () => {
    return (
        <div id="code-gorilla-article" className='code-gorilla-div'>
            <h1>Congratulations! </h1>
            <img src={Gorilla} alt="Code Gorilla" />
            <p>Your react component library is working!</p>
            <p>Go to <a href="https://www.code-gorilla.com/">Code-Gorilla.com</a> to read the full article!</p>
        </div>
    )
}

export default CodeGorillaArticle;

Create OtherArticle.js in components folder. This is to demonstrate multiple components in your library.
import React from 'react';
import './code-gorilla-package.css';

const OtherArticle = () => {
    return (
        <div id="code-gorilla-article" class='code-gorilla-div'>
            <h1>Another Component!</h1>
            <p>Your react component library is working!</p>
            <p>Learn more about webpack <a href="https://webpack.js.org/">https://webpack.js.org/</a></p>
        </div>
    )
}

export default OtherArticle;

Create index.js in src folder. This will expose the src/components externally.
import CodeGorillaArticle from "./components/CodeGorillaArticle";
import OtherArticle from "./components/OtherArticle";

export { CodeGorillaArticle, OtherArticle };

Configure Babel

Because webpack will still be using Babel to compile the JavaScript we should configure it to support React. Just as before, we will create a babel.config.js file in the root folder of the project as follows:
module.exports = {
    presets: [
        "@babel/preset-env", 
        "@babel/preset-react"
    ]
};
To learn more about Babel presets: https://babel.dev/docs/presets

Configure webpack

To configure webpack for the build, we will create a webpack.config.js file in the root folder of the project as follows:
const path = require('path');

module.exports = {
    entry: './src/index.js',
    mode: 'production',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'cg-react-webpack-asset-lib.js',
        library: {
            name: "cg-react-webpack-asset-lib",
            type: "umd"
        },
    },
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/,
                exclude: /node_modules/,
                use: ['babel-loader']
            },
            {
                test: /\.css$/i,
                use: ["style-loader", "css-loader"],
            },
            {
                test: /\.(png|svg|jpg|jpeg|gif)$/i,
                // Convert images to inline base64
                type: 'asset/inline'
            },
        ],
    },
    externals: {
        react: 'react'
    },
};
Here is a breakdown of the webpack.config.js contents:
  • entry tells webpack where the source code main entry point is.
  • output/filename determines the name of the output bundle.
  • output/library/name determines the name of the library that will be published.
  • output/library/type determines how the library can be used. Universal Module Definition (UMD) type will bundle the code to work with CommonJS, AMD and script tag.
  • module/rules/test uses regex patterns to identify file filters that apply to the loader used while bundling the code.
  • module/rules/exclude identifies folders that may match the file filter that should be excluded from the loader.
  • module/rules/use determines the loader that is used on the identified files.
  • module/rules/type determines the Asset Module type to apply. This is new to webpack 5 to replace the loaders.
  • externals identifies modules being used in your library that you do not want to bundle into your library. They will be identified as dependencies by the application using your library.

Asset Modules asset/inline type

The type:'asset/inline is the KEY part of this article. The asset/inline tells webpack to convert the images (identified by the test regex pattern) to be converted into Base64 and inserted directly into the built output code.

url-loader was used prior to webpack 5

Prior to webpack 5, you could accomplish similiar results to inline using the url-loader module.

First you would need to install the url-loader:
npm install url-loader

Then you would replace the rule in the webpack.config.js file as follows:
// url-loader works, but is older tech
    loader: "url-loader",
        options: {
            limit: Infinity
        }
Do not put the above url-loader code into your project, this is only for demonstration.

asset/resource won't work for libraries

At the time of writing this article, I was unable to use the asset/resource for the library. This is because the library would prepare the images in the distribution folder. The images would then be distributed into the node_modules folder of the application using the library and the images would not be available, without copying the images into another location accessible from the application. You could use an automation toolkit such as gulp to accomplish this for you when neccessary. This would be something you may want to do if you have large image files or want to provide other assets, such as files.

Build Distribution Code

Once all of the code is created we’ll want to compile it into JavaScript.

Build Script
To do this we will update the package.json to support the build as follows:
"scripts": {
"build": "webpack",
"test": "echo \"Error: no test specified\" && exit 1"
},
The test script placeholder should already exist from the npm init. We will be adding the build script. The results of the build will place the distribution code into a newly created “dist” folder.


Main Entry Point
Finally, you should update the “main” value in the package.json to provide the path to the distribution so that applications installing the code will know where the components are at.
"main": "dist/cg-react-webpack-asset-lib.js",

Run Build
Build the distribution files, this will create the dist folder and put the compiled JavaScript into it.
npm run build

Your library is now fully built and ready to use. Before we publish to the npm registry or git we can test locally, which is what we will be doing in the following steps.

Create Application

Now we will be creating a simple React application that will utilize our library.

Create a new directory for your library
mkdir cg-react-webpack-asset-app
cd cg-react-webpack-asset-app

Create the react application:
npx create-react-app cg-react-webpack-asset-app
cd cg-react-webpack-asset-app

Locally Install Library for Testing

Before we attempt to use the library from the npm repository or git, we can install the library locally as follows:
npm install path/to/cg-react-webpack-asset-lib
The path/to/cg-react-webpack-asset-lib value should be replaced with your directory location for the library created above.

Another alternative would be to create a link that can be accessed globally by going into the root of the library directory and executing the following:
npm link

Modify Application to use Library Components

In src folder, delete everything except:
  • index.js
  • App.js

Update App.js

Replace the contents of App.js with the following:
import { CodeGorillaArticle } from 'cg-react-webpack-asset-lib';

function App() {
  return (
    <div className="App">
     <p>Display the component:</p>
     <CodeGorillaArticle />
    </div>
  );
}

export default App;

Run Application using Local Library
npm start

Once the application is running, it should launch and web browser to display the following:

Git Commit and/or Publish

At this point the library has been tested and ready to commit and/or publish. You should now commit your code to your git repository.

If you have an npm account and have permission to publish your code you can do it now with:
npm publish

If you do not have permission, or if you simply don’t want to publish to the npm registry, you can still use the library directly from git, which is what we will be doing in the next step, where we will be building a React application to utilize our new library.

Install Library from Git

After confirming that everything is working locally we can now install from git.

First we should uninstall our local library:
npm uninstall cg-react-webpack-asset-lib

Now, we can finally install our library from git using the git+ prefix on your git repository. I have committed this code as a public git repository, so you could also install using my code using the following command:
npm install git+https://CodeGorilla@bitbucket.org/CodeGorilla/cg-react-webpack-asset-lib.git

Install from NPM registry

If you published your library to the npm registry you can simply install using the unique name that you published with. I have published the library from this article into the npm registry, which you could try as follows:
npm install cg-react-webpack-asset-lib

Conclusion

So if you’re writing multiple React applications and you find yourself copying and pasting the same code into each project, you can now place them into a library and simply install it into your new projects. And the added bonus is that as you’re improving your library by fixing patches and just making it more awesome, your applications are automatically being updated when they are rebuilt.

References


Resources

Here are the images (right click and save) I've used for this project if you want to repeat. Or you could download from git:


 
Return to articles