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?
@rollup/plugin-image
converts images to base64 which is an easy solution but has at least two flaws:
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
.