Skip to content

React Component Library

Build a production-ready React component library with Bunup in minutes. Zero config. Just works.

Quick Start

Scaffold a minimal starter or publish-ready React component library in seconds:

sh
bunx @bunup/cli@latest create

Select React Component Library from the options. Now you're ready to build components.

Creating Components

Create your first component:

src/components/button.tsx
tsx
export function Button(props: React.ComponentProps<'button'>): React.ReactNode {
  return <button type="button" {...props} />
}

Export it from your entry point:

src/index.tsx
tsx
export { Button } from './components/button'

Build it:

bash
bunx bunup

Your component is now compiled in dist/index.js with TypeScript declarations in dist/index.d.ts.

Styling Options

Bunup supports multiple styling approaches out of the box. Choose what works best for your library.

Pure CSS

Import CSS directly in your components. Bunup bundles everything automatically.

src/styles.css
css
[data-slot="button"] {
  background: hsl(211, 100%, 50%);
  color: white;
  padding: 0.6rem 1.2rem;
  border: none;
  border-radius: 0.5rem;
  cursor: pointer;
}

[data-slot="button"]:hover {
  background: hsl(211, 100%, 45%);
}
src/components/button.tsx
tsx
export function Button(props: React.ComponentProps<'button'>): React.ReactNode {
  return <button type="button" data-slot="button" {...props} />
}
src/index.tsx
tsx
import './styles.css'

export { Button } from './components/button'

Your CSS is automatically bundled into dist/index.css with cross-browser compatibility. Learn more about CSS support.

CSS Modules

Get automatic class name scoping with CSS modules. Just use .module.css:

src/components/button.module.css
css
.button {
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  color: white;
}

.primary {
  background-color: #007bff;
}

.primary:hover {
  background-color: #0056b3;
}
src/components/button.tsx
tsx
import styles from './button.module.css'

export function Button(props: React.ComponentProps<'button'>): React.ReactNode {
  return (
    <button
      type="button"
      className={`${styles.button} ${styles.primary}`}
      {...props}
    />
  )
}

TypeScript definitions are generated automatically - you get full autocomplete and type safety. Learn more about CSS modules.

Tailwind CSS

Use Tailwind CSS v4 with zero PostCSS configuration. Your components work everywhere - consumers don't need Tailwind installed.

Install the Tailwind CSS plugin:

bash
bun add --dev @bunup/plugin-tailwindcss

Add it to your config:

bunup.config.ts
ts
import { defineConfig } from 'bunup'
import { tailwindcss } from '@bunup/plugin-tailwindcss'

export default defineConfig({
  plugins: [tailwindcss()],
})

Create your styles with a scoped prefix to prevent conflicts:

src/styles.css
css
@import "tailwindcss" prefix(mylib);

Use prefixed classes in your components:

src/components/button.tsx
tsx
export function Button(props: React.ComponentProps<'button'>): React.ReactNode {
  return (
    <button
      type="button"
      className="mylib:bg-blue-500 mylib:hover:bg-blue-600 mylib:text-white mylib:px-4 mylib:py-2 mylib:rounded-md"
      {...props}
    />
  )
}
src/index.tsx
tsx
import './styles.css'

export { Button } from './components/button'

The plugin outputs scoped, tree-shaken CSS. Only the classes you use are included, and the prefix prevents conflicts with consumer applications. Learn more about the Tailwind CSS plugin.

Distribution

Configure your package.json for npm publishing:

package.json
json
{
  "name": "my-component-library",
  "version": "1.0.0",
  "type": "module",
  "files": [
    "dist"
  ],
  "module": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "import": {
        "types": "./dist/index.d.ts",
        "default": "./dist/index.js"
      }
    },
    "./styles.css": "./dist/index.css",
    "./package.json": "./package.json"
  },
  "peerDependencies": {
    "react": "^18.0.0 || ^19.0.0",
    "react-dom": "^18.0.0 || ^19.0.0"
  }
}

Consumers import your library like this:

tsx
import 'my-component-library/styles.css'
import { Button } from 'my-component-library'

function App() {
  return <Button>Click me</Button>
}

Inject Styles Optional

Want to skip the separate CSS import? Use the inject styles option to bundle CSS directly into JavaScript:

sh
bunup --css.inject
ts
import { defineConfig } from 'bunup'

export default defineConfig({
  css: {
    inject: true,
  },
})

Or with the Tailwind CSS plugin:

bunup.config.ts
ts
import { defineConfig } from 'bunup'
import { tailwindcss } from '@bunup/plugin-tailwindcss'

export default defineConfig({
  plugins: [
    tailwindcss({
      inject: true,
    })
  ],
})

Now consumers only need to import your components:

tsx
import { Button } from 'my-component-library'

function App() {
  return <Button>Click me</Button>
}

Styles are automatically injected at runtime.

Examples

Check out complete examples in the examples directory:

Released under the MIT License.