---
title: Using Bamboo in a Component Library
description: How to set up Bamboo in a component library
---

When creating a component library that uses Bamboo which can be used in a variety of different projects, you have four
options:

1. Ship a Bamboo [preset](/docs/customization/presets)
2. Ship a static CSS file
3. Use Bamboo as external package, and ship the src files
4. Use Bamboo as external package, and ship the build info file

> In the examples below, we use `tsdown` as the build tool. You can use any other build tool.

## Recommendations

- If your library code shouldn't be published on npm and App code uses Bamboo, use
  [ship build info](#ship-the-build-info-file) approach
- If your app code doesn't use Bamboo, use the [static css](#ship-a-static-css-file) file approach
- If your app code lives in a monorepo, use the [include src files](#include-the-src-files) approach
- If your library code doesn't ship components but only ships tokens, patterns or recipes, use the
  [ship preset](#ship-a-bamboo-preset) approach

> ⚠️ If you use the `include src files` or `ship build info` approach, you might also need to ship a `preset` if your
> library code has any custom tokens, patterns or recipes.

## Ship a Bamboo Preset

This is the simplest approach. You can include the token, semantic tokens, text styles, etc. within a preset and consume
them in your projects.

**Library code**

```tsx filename="src/index.ts"
import { definePreset } from '@bamboocss/dev'

export const acmePreset = definePreset({
  theme: {
    extend: {
      tokens: {
        colors: { primary: { value: 'blue.500' } },
      },
    },
  },
})
```

Build the preset code

```bash
pnpm tsdown src/index.ts
```

**App code**

```tsx filename="bamboo.config.ts"
import { acmePreset } from '@acme-org/bamboo-preset'
import { defineConfig } from '@bamboocss/dev'

export default defineConfig({
  //...
  presets: ['@bamboocss/dev/presets', acmePreset],
})
```

> Adding a preset will remove the default theme from Bamboo. To add it back, you need to include the
> `@bamboocss/dev/presets` preset.

## Ship a Static CSS File

This approach involves extracting the static css of your library at build time. Then you can import the CSS file in your
app code.

**Library code**

```tsx filename="src/index.tsx"
import { css } from '../styled-system/css'

export function Button({ children }) {
  return (
    <button type="button" className={css({ bg: 'red.300', px: '2', py: '3' })}>
      {children}
    </button>
  )
}
```

Then you can build the library code and generate the static CSS file:

```bash
# build the library code
tsdown src/index.tsx

# generate the static CSS file
bamboo cssgen --outfile dist/styles.css
```

Finally, don't forget to include the [cascade layers](/docs/concepts/cascade-layers) as well in your app code:

**App code**

```tsx filename="src/App.tsx"
import { Button } from '@acme-org/design-system'
import './main.css'

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

**main.css**

```css filename="src/main.css"
@layer reset, base, tokens, recipes, utilities;
@import url('@acme-org/design-system/dist/styles.css');

/* Your own styles here */
```

This approach comes with a few downsides:

- You can't customize the styles since the css is already generated
- You might need add the [prefix](/docs/references/config#prefix) option to avoid className conflicts

  ```tsx filename="bamboo.config.ts"
  import { defineConfig } from '@bamboocss/dev'

  export default defineConfig({
    //...
    prefix: 'acme',
  })
  ```

- You might have duplicate CSS classes when using multiple atomic css libraries

## Use Bamboo as external package

### Summary

- create a Bamboo [preset](/docs/customization/presets) so that you (and your users) can share the same design system
  tokens
- create a workspace package for your outdir (`@acme-org/styled-system`) and use that package name as the `importMap` in
  your app code
- have your component library (`@acme-org/components`) use the `@acme-org/styled-system` package as external

De-coupling the component library from the `styled-system` lets your users share one copy of the runtime between your
library and their app code, and lets them pass styles in:

```tsx filename="component-lib/src/button.tsx"
import { css } from '@acme-org/styled-system/css'

export function Button({ children, css: cssProp }) {
  return (
    <button type="button" className={css({ bg: 'red.300', px: '2', py: '3' }, cssProp)}>
      {children}
    </button>
  )
}
```

```tsx filename="app/src/App.tsx"
import { Button } from '@acme-org/design-system'

export function App() {
  return <Button css={{ color: 'white' }}>Click me</Button>
}
```

---

Let's make a dedicated workspace package for your `outdir`:

1. Create a new directory `packages/styled-system` (or any other name)
2. Install `@bamboocss/dev` as a dev dependency
3. Run the `bamboo init` command in there to generate a `bamboo.config.ts` file, don't forget to set the `importMap` if
   needed
4. [optional] you might want to install and import your [preset](/docs/customization/presets) in this `bamboo.config.ts`
   file as well
5. Run the [`bamboo emit-pkg`](/docs/references/cli#emit-pkg) command to set the entrypoints in
   [`exports`](https://nodejs.org/api/packages.html#exports)

This should look similar to this:

```json
{
  "name": "@acme-org/styled-system",
  "version": "1.0.0",
  "exports": {
    "./css": {
      "types": "./css/index.d.ts",
      "require": "./css/index.mjs",
      "import": "./css/index.mjs"
    },
    "./tokens": {
      "types": "./tokens/index.d.ts",
      "require": "./tokens/index.mjs",
      "import": "./tokens/index.mjs"
    },
    "./types": {
      "types": "./types/index.d.ts",
      "require": "./types/index.mjs",
      "import": "./types/index.mjs"
    },
    "./patterns": {
      "types": "./patterns/index.d.ts",
      "require": "./patterns/index.mjs",
      "import": "./patterns/index.mjs"
    },
    "./recipes": {
      "types": "./recipes/index.d.ts",
      "require": "./recipes/index.mjs",
      "import": "./recipes/index.mjs"
    },
    "./styles.css": "./styles.css"
  },
  "devDependencies": {
    "@bamboocss/dev": "^1.28.0",
    "@types/react": "19.2.2",
    "react": "^19.2.0"
  },
  "peerDependencies": {
    "react": ">=19"
  }
}
```

> Notice that we've included the `react` and its corresponding `@types/react` in the `devDependencies` and
> `peerDependencies`. This is to ensure the types are correctly set up in the `styled-system` package.

Going forward, you'll now import the functions from the `@acme-org/styled-system` monorepo package.

```tsx
import { css } from '@acme-org/styled-system/css'

export function Button({ children }) {
  return (
    <button type="button" className={css({ bg: 'red.300', px: '2', py: '3' })}>
      {children}
    </button>
  )
}
```

**App code**

Install the newly created `@acme-org/styled-system` package in your app code.

```bash
pnpm add @acme-org/styled-system
```

Configure the `importMap` in your `bamboo.config.ts` to match the `name` field of your outdir `package.json`. This will
inform Bamboo which imports belong to the `styled-system`.

```tsx filename="bamboo.config.ts"
import { defineConfig } from '@bamboocss/dev'

export default defineConfig({
  //...
  importMap: '@acme-org/styled-system',
  outdir: 'styled-system',
})
```

Mark the `@acme-org/styled-system` as an external package in your library build tool. This ensures that the generated JS
runtime code is imported only once, avoiding duplication.

```bash
tsdown src/index.tsx --external @acme-org/styled-system
```

Both variants below share that setup. They differ only in what the app's `include` points at.

### Include the src files

Include the `src` directory from the library code in the bamboo config.

```tsx {6} filename="bamboo.config.ts"
import { defineConfig } from '@bamboocss/dev'

export default defineConfig({
  //...
  include: ['../@acme-org/design-system/src/**/*.tsx', './src/**/*.{ts,tsx}'],
  importMap: '@acme-org/styled-system',
  outdir: 'styled-system',
})
```

### Ship the build info file

This approach is similar to the previous one, but instead of shipping the source code, you ship the Bamboo build info
file. This will have **the exact same end-result** as adding the sources files in the `include`, but it will allow you
not to ship the source code.

The build info file is a JSON file that **only** contains the information about the static extraction result, you still
need to ship your app build/dist by yourself. It can be used by Bamboo to generate CSS classes without the need for
parsing the source code.

Generate the build info file:

```bash
bamboo ship --outfile dist/bamboo.buildinfo.json
```

Next, you need to include the build info file from the library code in the bamboo config.

```tsx {6} filename="bamboo.config.ts"
import { defineConfig } from '@bamboocss/dev'

export default defineConfig({
  //...
  include: ['./node_modules/@acme-org/design-system/dist/bamboo.buildinfo.json', './src/**/*.{ts,tsx}'],
  importMap: '@acme-org/styled-system',
  outdir: 'styled-system',
})
```

## FAQ

### How to override tokens used by the `@acme-org/styled-system` package?

You can override the tokens used by the `@acme-org/styled-system` package by extending the `theme` in your
`bamboo.config.ts` file.

```tsx filename="bamboo.config.ts"
import { defineConfig } from '@bamboocss/dev'

export default defineConfig({
  //...
  presets: ['@acme-org/preset']
  theme: {
    extend: {
      tokens: {
        colors: { primary: { value: 'blue.500' } }
      }
    }
  }
})
```

## Troubleshooting

- When using `tsdown` or any other build tool for your component library, if you run into a module resolution error that
  looks similar to `ERROR: Could not resolve "../styled-system/xxx"`. Consider setting the `outExtension`in the bamboo
  config to`js`

- If you use Yarn PnP, you might need to set the `nodeLinker: node-modules` in the `.yarnrc.yml` file.
