Updated
—
5 min read

I recently published react-google-reviews, a library for integrating Google reviews with ReactJS.
And in publishing this library, I originally wanted to use TailwindCSS for styling the components in the library.
So here's a step-by-step guide on bundling a React library that uses TailwindCSS using Rollup.js. Let's dive right in!
Create a new project folder:
mkdir rollup-tailwind cd rollup-tailwind
Initialize npm package:
npm init -y
For this tutorial, we'll have a simple folder structure:
- package.json - src - components - index.ts - Button.tsx - index.ts
Our Button.tsx will use Tailwind classes like so:
import React from "react"; function Button() { return ( <button className="outline bg-blue-500 text-white"> Hello World </button> ); }
In components/index.ts we'll export our Button:
export { default as Button } from "./Button";
In src/index.ts, our entry file, we will similarly export the Button:
import { Button } from "./components"; export { Button };
Lastly, let's install some dependencies:
npm i -D react react-dom rollup tailwindcss typescript @types/react @types/react-dom @rollup/plugin-typescript @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-terser rollup-plugin-postcss postcss tslib autoprefixer rollup-plugin-dts
We'll be using several Rollup plugins, as well as installing TailwindCSS and PostCSS.
Now let's create our rollup.config.js file:
import commonjs from "@rollup/plugin-commonjs"; import resolve from "@rollup/plugin-node-resolve"; import terser from "@rollup/plugin-terser"; import typescript from "@rollup/plugin-typescript"; import postcss from "rollup-plugin-postcss"; import dts from "rollup-plugin-dts"; const packageJson = require("./package.json"); export default [ { input: "src/index.ts", output: [ { file: packageJson.main, format: "cjs", sourcemap: true, }, { file: packageJson.module, format: "esm", sourcemap: true, }, ], plugins: [ resolve({ ignoreGlobal: false, include: ['node_modules/**'], skip: ['react', 'react-dom'], }), commonjs(), typescript({ tsconfig: "./tsconfig.json" }), postcss({ extract: true, minimize: true, }), terser(), ], }, { input: "dist/esm/types/index.d.ts", output: [{ file: "dist/index.d.ts", format: "esm" }], plugins: [dts.default()], external: [/\.css$/], }, ];
It's a simple setup. We define our entry point, and the output files will point to our package.json.
We also install several plugins that will be necessary, most importantly rollup-plugin-postcss.
It's important to skip react-dom and react in the node resolve plugin:
plugins: [ resolve({ ignoreGlobal: false, include: ['node_modules/**'], skip: ['react', 'react-dom'], // skip these }), // ... rest of plugins as above ]
Skipping these dependencies is necessary to avoid errors like Cannot read properties of null (reading 'useRef').
Similarly, since we're building a React library, we should move react-dom and react to peerDependencies in our package.json:
{ "peerDependencies": { "react": "^18.3.1", // or whatever version(s) "react-dom": "^18.3.1", } }
By setting react and react-dom as externals in our rollup configuration, we avoid these errors caused by having duplicate React versions.
Now let's get back to the project.
Our rollup.config.js references our main and module properties of our package.json.
So let's modify our package.json scripts like so:
{ // ... "scripts": { "rollup": "rollup -c --bundleConfigAsCjs" }, "main": "dist/cjs/index.js", // cjs output "module": "dist/esm/index.js", // esm output "files": [ "dist" ], // ... }
We've added a script to run Rollup and also defined two output files for CommonJS and ES Modules, respectively.
Since we're using Typescript, let's initialize a tsconfig.json:
{ "compilerOptions": { "target": "es5", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "lib": ["dom", "dom.iterable", "esnext"], "skipLibCheck": true, "jsx": "react", "module": "ESNext", "declaration": true, "declarationDir": "types", "sourceMap": true, "outDir": "dist", "moduleResolution": "node", "allowSyntheticDefaultImports": true, "emitDeclarationOnly": true, }, "include": ["src"], }
Pretty simple setup, but note that our outDir points to our dist directory.
Now we're ready to integrate TailwindCSS into our Rollup bundle process.
You should already have Tailwind installed from previously, but if not:
npm i -D tailwindcss postcss autoprefixer
Then initialize TailwindCSS:
npx tailwindcss init
Modify your tailwind.config.js as you would normally to define the file types:
/** @type {import('tailwindcss').Config} */ module.exports = { content: [ "./src/**/*.tsx" ], theme: { extend: {}, }, plugins: [], }
And now we'll create a main.css located in src/styles/main.css:
@tailwind base; @tailwind components; @tailwind utilities;
We're almost done, now let's finish up our Rollup config.
The final step is to modify our rollup.config.js by adding this object to our exported array:
{ // ... same as above input: "src/styles/main.css", // add the `main.css` file input output: [{ file: "dist/index.css", format: "es" }], plugins: [ postcss({ extract: true, minimize: true, }), ], // ... same as above },
Here we are pointing to our main.css file and using PostCSS to bundle a dist/index.css file which will contain our compiled TailwindCSS styles.
If you run Rollup now:
npm run rollup
You should see a dist folder be generated as follows:
- dist - cjs - esm - index.css
In our Button.tsx component, we had used three classes: outline, bg-blue-500 and text-white.
We can verify whether Rollup bundled our Tailwind styles by opening up dist/index.css:
.bg-blue-500{--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity))} .text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))} .outline{outline-style:solid}
And it worked!
We can confirm this further by running npm pack and installing the package onto a blank NextJS app.
Then we import and use the component like so:
import { Button } from "rollup-tailwind"; import "rollup-tailwind/dist/index.css"; // remember to import styles export default function Home() { return ( <div style={{ padding: "20px" }}> <Button></Button> </div> ); }
Remember to import the index.css file.
And we see that our Tailwind classes have been bundled using Rollup:

Integrating TailwindCSS with Rollup isn't too challenging, but requires some knowledge of configuration.
We use PostCSS and Rollup plugins to bundle our Tailwind styles into our package for distribution.
Hope this helps!
Join my newsletter for lessons, experiments, and failures in bootstrapping online businesses.
Sign up if you're curious. I’ll only email you if it's actually good.

Meet the Author
Ryan Chiang
Hello, I'm Ryan. I build things and write about them. This is my blog of my learnings, tutorials, and whatever else I feel like writing about.
What I'm currently building →.