Tailwind CSS

React components with Tailwind CSS integration

Tailwind CSS

Style React components with Tailwind CSS. Tailwind runs through PostCSS as part of Packem's CSS pipeline, so utility classes are compiled and the resulting stylesheet is extracted alongside your components.

Overview

This example demonstrates:

  • React components using Tailwind utility classes
  • Tailwind CSS v4 wired in through PostCSS
  • A single extracted stylesheet for the whole library
  • The react preset for JSX handling

Tailwind is processed by the PostCSS loader from @visulima/rollup-plugin-css, configured through Packem's rollup.css option.

Installation

npm install --save-dev tailwindcss @tailwindcss/postcss postcss

Project Structure

react-tailwind-lib/
├── src/
│   ├── Button.tsx
│   ├── styles.css      # Tailwind entry stylesheet
│   └── index.ts
├── package.json
├── packem.config.ts
└── tsconfig.json

Component Implementation

src/styles.css — import Tailwind

@import 'tailwindcss';

src/Button.tsx

'use client'

import React from 'react'

import './styles.css'

export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: 'primary' | 'secondary'
  children: React.ReactNode
}

export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  ({ variant = 'primary', children, ...props }, ref) => {
    const base =
      'inline-flex items-center justify-center rounded-md px-4 py-2 font-medium'
    const variants = {
      primary: 'bg-blue-600 text-white hover:bg-blue-700',
      secondary: 'bg-gray-600 text-white hover:bg-gray-700',
    }

    return (
      <button
        ref={ref}
        className={`${base} ${variants[variant]}`}
        {...props}
      >
        {children}
      </button>
    )
  }
)

Button.displayName = 'Button'

src/index.ts

export { Button } from './Button'
export type { ButtonProps } from './Button'

Configuration

package.json

{
  "name": "@myorg/react-tailwind",
  "version": "1.0.0",
  "type": "module",
  "files": [
    "dist"
  ],
  "main": "./dist/index.cjs",
  "module": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js",
      "require": "./dist/index.cjs"
    },
    "./styles.css": "./dist/styles.css"
  },
  "sideEffects": [
    "**/*.css"
  ],
  "scripts": {
    "build": "packem build",
    "dev": "packem build --watch"
  },
  "peerDependencies": {
    "react": ">=16.8.0",
    "react-dom": ">=16.8.0"
  },
  "devDependencies": {
    "@visulima/packem": "^2",
    "@tailwindcss/postcss": "^4.0.0",
    "@types/react": "^18.0.0",
    "@types/react-dom": "^18.0.0",
    "postcss": "^8.4.0",
    "react": "^18.0.0",
    "react-dom": "^18.0.0",
    "tailwindcss": "^4.0.0",
    "typescript": "^5.3.0"
  }
}

Exposing the compiled stylesheet through an "./styles.css" export lets consumers import it once: import '@myorg/react-tailwind/styles.css'.

Packem Configuration

Enable the React preset and register Tailwind as a PostCSS plugin. mode: 'extract' writes the compiled utilities to a single .css file:

import { defineConfig } from '@visulima/packem/config'
import transformer from '@visulima/packem/transformer/esbuild'

export default defineConfig({
  preset: 'react',
  transformer,
  sourcemap: true,
  declaration: true,
  rollup: {
    css: {
      mode: 'extract',
      postcss: {
        plugins: ['@tailwindcss/postcss'],
      },
    },
  },
})

If you prefer an explicit PostCSS config file, point the loader at it instead:

// postcss.config.js
export default {
  plugins: {
    '@tailwindcss/postcss': {},
  },
}
import { defineConfig } from '@visulima/packem/config'
import transformer from '@visulima/packem/transformer/esbuild'
import postcssLoader from '@visulima/packem/css/loader/postcss'

export default defineConfig({
  preset: 'react',
  transformer,
  rollup: {
    css: {
      mode: 'extract',
      loaders: [postcssLoader],
    },
  },
})

TypeScript Configuration

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "jsx": "react-jsx",
    "declaration": true,
    "outDir": "dist",
    "rootDir": "src",
    "strict": true,
    "skipLibCheck": true,
    "esModuleInterop": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

Building

npm run build

Output

dist/
├── index.js     # ESM entry
├── index.cjs    # CJS entry
├── index.d.ts   # TypeScript declarations
└── styles.css   # Compiled Tailwind utilities

Usage

import { Button } from '@myorg/react-tailwind'
import '@myorg/react-tailwind/styles.css'

function App() {
  return <Button variant="primary">Click me</Button>
}

Tailwind Oxide (Alternative)

Packem also ships a dedicated Tailwind loader backed by Tailwind's Rust-based Oxide engine. See the CSS guide for details on the @visulima/packem/css/loader/tailwindcss loader and the required @tailwindcss/node / @tailwindcss/oxide dependencies.

Next Steps

Support

Contribute to our work and keep us going

Community is the heart of open source. The success of our packages wouldn't be possible without the incredible contributions of users, testers, and developers who collaborate with us every day.Want to get involved? Here are some tips on how you can make a meaningful impact on our open source projects.

Ready to help us out?

Be sure to check out the package's contribution guidelines first. They'll walk you through the process on how to properly submit an issue or pull request to our repositories.

Submit a pull request

Found something to improve? Fork the repo, make your changes, and open a PR. We review every contribution and provide feedback to help you get merged.

Good first issues

Simple issues suited for people new to open source development, and often a good place to start working on a package.
View good first issues