Search code examples
reactjsrollup

Issue with bundling images in React component library using Rollup


I am working on a React component library and I am using Rollup as the bundler. But I am having a problem with bundling the images in the library. I have 4 image files that I have used (visaLogo.png, mastercardLogo.png, cardBackgroundVisa.png, and cardBackgroundMasterCard.png). When I run the component libary in Storybook the images load correctly. But when I install the library in another bare React app, only the mastercardLogo.png is shown, the others are missing.

This is my CreditCardForm.tsx:

import React, { useState } from "react";
import "./CreditCardForm.css";
import mastercardLogo from "../../../images/mastercardLogo.png";
import visaLogo from "../../../images/visaLogo.png";
import cardBackgroundMasterCard from "../../../images/cardBackgroundMasterCard.png";
import cardBackgroundVisa from "../../../images/cardBackgroundVisa.png";

interface CreditCardFormProps {
  name: string;
  onChangeName: () => void;
  cardNumber: string;
  onChangeCardNumber: () => void;
  expirationDate: string;
  onChangeExpirationDate: () => void;
  cvv: string;
  onChangeCvv: () => void;
  cardType: string;
  onChangeCardType: () => void;
  onSubmit: () => void;
}

const CreditCardForm = (props: CreditCardFormProps) => {
  const [cardType, setCardType] = useState("mastercard");

  const onChangeCardType = (e: React.ChangeEvent<HTMLInputElement>) => {
    setCardType(e.target.value);
  };

  return (
    <>
      <div
        className="card-container"
        style={{
          backgroundImage: `${
            cardType === "visa"
              ? `url(${cardBackgroundVisa})`
              : `url(${cardBackgroundMasterCard})`
          }`,
        }}
      >
        <div className="card-type">
          <img
            src={cardType === "visa" ? visaLogo : mastercardLogo}
            alt="logo"
            className="logo-style"
          />
        </div>

        <div className="card-number">
          <p>4829 0921 8391 4458</p>
        </div>

        <div className="card-info">
          <div className="card-name">
            <p className="title">Cardholder name</p>
            <p className="info">John Doe</p>
          </div>

          <div className="card-expiration">
            <p className="title">Valid Thru</p>
            <p className="info">01/26</p>
          </div>
        </div>
      </div>

//and the form inputs etc...
  
    </>
  );
};

export default CreditCardForm;

And this is my rollup.config.js file:

import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import typescript from "@rollup/plugin-typescript";
import dts from "rollup-plugin-dts";
import packageJson from "./package.json" assert { type: "json" }; 
import external from "rollup-plugin-peer-deps-external";
import postcss from "rollup-plugin-postcss";
import url from 'rollup-plugin-url';
import image from "@rollup/plugin-image";


export default [

  {
    input: "src/index.ts", 
    output: [
      {
        file: packageJson.main,
        format: "cjs",
        sourcemap: true,
      }, 
      {
        file: packageJson.module,
        format: "esm",
        sourcemap: true,
      }, 
    ],
    plugins: [
      resolve(),
      commonjs(),
      typescript({ tsconfig: "./tsconfig.json" }),
      external(),
      postcss(), 
      url(),
      image(),


    ], 
  },

  
  {
    input: "dist/esm/types/index.d.ts",
    output: [
      { file: "dist/index.d.ts", format: "esm", globals: { react: "React" } },
    ],
    plugins: [dts()],
    external: ["react", "react-dom",/\.css$/ ],
  },
];

What could be the issue and how can I fix this?


Solution

  • @rollup/plugin-image converts images to base64 which is an easy solution but has at least two flaws:

    • doesn't allow the application which uses the library to decide how to handle the images,
    • the conversion seems to be unreliable; for us it worked until we've added a praticular image whose converted form was somehow broken.

    I've found another plugin to solve this: rollup-plugin-rebase. It moves assets other than JS to the destination folder and adjusts paths to those assets in the JS files. The paths are abolute though, so I had to add makeAbsoluteExternalsRelative: true to the Rollup config. I found this option in this issue and its documentation is here.

    This is what Rollup config looks like now (narrowed to the relevant parts):

    import rebase from "rollup-plugin-rebase"
    //...
    
    export default [
      {
        input:   "src/index.js",
        output:  [
          {
            file:      "dist/cjs/index.js",
            format:    "cjs",
            sourcemap: true,
          },
          {
            file:      "dist/esm/index.js",
            format:    "esm",
            sourcemap: true,
          },
        ],
        makeAbsoluteExternalsRelative: true,  //necessary for rollup-plugin-rebase to work properly
        plugins: [
          //...
          rebase({
            assetFolder: 'assets',
            keepName: true,
          }),
          //...
        ],
      },
    ]
    
    

    Now, images end up in dist/cjs/assets and dist/esm/assets.