---
title: Migrating from Panda CSS
description: Migrate your project from Panda CSS to Bamboo CSS.
---

Bamboo CSS started as a fork of [Panda CSS](https://panda-css.com/), so unlike the other guides in this section this one
is not a rewrite. The styling API is the same: your style objects, recipes, patterns, conditions and tokens carry over
unchanged. What changes is the name of the package they come from.

For most projects this is a find-and-replace of `panda` with `bamboo`, plus a re-run of codegen.

## What stays the same

- `css`, `cva`, `sva`, `cx` and the pattern functions – same names and authoring signatures. Under Vite they are
  compile-time APIs: recipe selections share the same global declaration atoms as `css()`
  ([below](#vite-compiles-recipes-to-shared-atoms)).
- Recipes, variants, compound variants and default variants. **Slot recipes are the exception** – their output compiles
  to one atom string per selected slot; see [below](#vite-compiles-slot-selections-directly).
- Tokens, semantic tokens, conditions, breakpoints, themes, `staticCss`, cascade layers.
- The config shape, minus five keys. Every key you have in `panda.config.ts` is valid in `bamboo.config.ts` except
  `studio`, `jsxFramework`, `jsxFactory`, `jsxStyleProps` and `syntax`, none of which have an equivalent – delete them.
- The output directory. It still defaults to `styled-system`, so imports like `styled-system/css` do not move.

Because the generated output directory keeps its name and shape, the only imports you have to touch are the ones that
name the tool itself – the config file, the PostCSS plugin, and any direct `@pandacss/*` imports.

## Rename the packages

Every package moves from the `@pandacss` scope to `@bamboocss`. The ones a project normally depends on:

| Panda CSS                | Bamboo CSS                 |
| ------------------------ | -------------------------- |
| `@pandacss/dev`          | `@bamboocss/dev`           |
| `@pandacss/types`        | `@bamboocss/types`         |
| `@pandacss/preset-base`  | `@bamboocss/preset-base`   |
| `@pandacss/preset-panda` | `@bamboocss/preset-bamboo` |
| `@pandacss/postcss`      | `@bamboocss/postcss`       |

The internal packages (`@pandacss/core`, `/node`, `/parser`, `/generator`, `/token-dictionary` and the rest) follow the
same rule if you import them directly.

<Callout type="info">
  The default preset is the one rename that is not a straight scope swap: `@pandacss/preset-panda` becomes
  `@bamboocss/preset-bamboo`. If you never listed presets explicitly, the defaults are applied for you and there is
  nothing to change.
</Callout>

## Step by step

<Steps>

### Swap the dependencies

<Tabs items={['pnpm', 'npm', 'yarn', 'bun']}>
  {/* <!-- prettier-ignore-start --> */}
  <Tab>
    ```bash
    pnpm remove @pandacss/dev
    pnpm add -D @bamboocss/dev
    ```
  </Tab>
  <Tab>
    ```bash
    npm uninstall @pandacss/dev
    npm install -D @bamboocss/dev
    ```
  </Tab>
  <Tab>
    ```bash
    yarn remove @pandacss/dev
    yarn add -D @bamboocss/dev
    ```
  </Tab>
  <Tab>
    ```bash
    bun remove @pandacss/dev
    bun add -D @bamboocss/dev
    ```
  </Tab>
  {/* <!-- prettier-ignore-end --> */}
</Tabs>

### Rename the config file

`panda.config.ts` becomes `bamboo.config.ts`. Bamboo looks for `bamboo.config.{ts,js,mts,mjs}` and will not pick up a
file under the old name.

Update the import inside it, and the preset names if you listed them:

```ts
// bamboo.config.ts
import { defineConfig } from '@bamboocss/dev'

export default defineConfig({
  presets: ['@bamboocss/preset-base', '@bamboocss/preset-bamboo'],
  include: ['./src/**/*.{js,jsx,ts,tsx}'],
  outdir: 'styled-system',
})
```

### Update the PostCSS config

```js
// postcss.config.cjs
module.exports = {
  plugins: {
    '@bamboocss/dev/postcss': {},
  },
}
```

### Update your scripts

The CLI is `bamboo` (`bamboocss` also works). Every command keeps its name, so `panda codegen` becomes `bamboo codegen`,
`panda cssgen` becomes `bamboo cssgen`, and so on.

```json
{
  "scripts": {
    "prepare": "bamboo codegen"
  }
}
```

### Regenerate the output directory

Delete the existing `styled-system` directory and generate it again, so nothing from the previous install is left
behind:

```bash
rm -rf styled-system
pnpm bamboo codegen
```

</Steps>

## `cx` joins, in both tools

At runtime, both helpers join arbitrary class strings. Bamboo's Vite compiler additionally recognizes analyzable Bamboo
style arguments: it composes their declarations in argument order before allocating atoms, so overridden declarations do
not ship. A `cx()` containing an unknown external class remains the tiny joining helper. See
[Merging styles](/docs/concepts/merging-styles).

## Vite compiles slot selections directly

Panda's slot recipe output carries recipe- and variant-specific classes. Bamboo's Vite compiler resolves the selection
first, then returns one shared atom string per slot:

```tsx
const classes = checkbox({ size })
return (
  <div className={classes.root}>
    <span className={classes.label} />
  </div>
)
```

A static selection becomes a literal object. A finite runtime selection becomes a reduced lookup whose leaves are
complete slot objects. Recipe names, slot names, `scopeRoots`, and DOM shape do not enter the emitted selectors, so
portals require no CSS scoping accommodation. Use a stable `data-slot` attribute if application CSS or tests need a
semantic slot selector; generated atom names are intentionally opaque.

## Vite compiles recipes to shared atoms

With `@bamboocss/vite`, `cva()` and `sva()` are compile-time declarations. A selected recipe is resolved to authored
styles and shares declaration atoms with every other recipe and `css()` call:

```js
cva({ base: { padding: '4' }, variants: { size: { sm: { fontSize: 'sm' } } } })
// panda:  'p_4 fs_sm'
// bamboo: compact global atoms for padding:4 and fontSize:sm
```

The recipe name, source file, and `className` do not enter declaration identity. For analyzable `cx(recipe(), css())`,
Bamboo composes the StyleSets in argument order before allocating classes, so overrides do not depend on stylesheet
order.

Finite runtime axes become a reduced lookup whose leaves already include matching compound variants. The recipe config
and recipe-specific selectors do not ship. Extraction-only CLI and PostCSS output retains Bamboo's named recipe model.

## Things that are easy to miss

- **`PANDA_DEBUG`** is now `BAMBOO_DEBUG`. See [Debugging](/docs/guides/debugging).
- **There is no JSX factory.** `styled.div`, `panda.div`, `styled('button', recipe)`, `createStyleContext` and
  `splitCssProps` have no equivalent, and `styled-system/jsx` is not generated at all. Style props become a `css()`
  call, and a recipe component becomes an ordinary component that calls the recipe:

  ```tsx
  // panda
  const Button = styled('button', buttonRecipe)
  <Button size="sm" />

  // bamboo
  const Button = (props: ButtonProps) => {
    const [variantProps, rest] = buttonRecipe.splitVariantProps(props)
    return <button {...rest} className={cx(buttonRecipe(variantProps), props.className)} />
  }
  ```

  `jsxFramework`, `jsxFactory` and `jsxStyleProps` are gone from the config with it — delete them.

- **There are no JSX pattern components.** `<Stack>`, `<Box>`, `<HStack>` and the rest are not generated, and
  `styled-system/jsx` does not exist to import them from. The pattern **functions** are unchanged and are what those
  components wrapped, so the rewrite is mechanical:

  ```tsx
  // panda
  import { Stack } from '../styled-system/jsx'
  ;<Stack gap="4" align="center">
    {children}
  </Stack>

  // bamboo
  import { flex } from '../styled-system/patterns'
  ;<div className={flex({ direction: 'column', gap: '4', align: 'center' })}>{children}</div>
  ```

- **There is no template literal syntax.** ``css`color: red` ``, and the template form of `cva`, `sva` and the patterns,
  are not supported. Write the object form:

  ```tsx
  // panda
  const heading = css`
    font-size: 24px;
    font-weight: bold;
  `

  // bamboo
  const heading = css({ fontSize: '24px', fontWeight: 'bold' })
  ```

  The `syntax` config key is gone with it — both values — so delete it if you set it either way.

- **The ESLint plugin** is `@bamboocss/eslint-plugin`.
- **Editor and CI caches.** The TypeScript server holds on to the old `styled-system` types; restart it after
  regenerating.
- **`.gitignore`.** If you generate `styled-system` in CI rather than committing it, the entry does not change, since
  the directory name is the same.

## Worth a look once you have migrated

These are documented in full elsewhere, and none of them are required to get a migrated project building:

- [Source compilation](/docs/guides/source-transformation) – `@bamboocss/vite` resolves every style API into shared
  atoms or a finite recipe table, and rejects open runtime styling.
- [`prune.tokens`](/docs/references/config#prune) – drops token CSS variables nothing can reach, which is usually the
  largest single saving in render-blocking CSS. On by default.
- [`preflight.prune`](/docs/references/config#preflightprune) – drops the parts of the reset that style elements your
  source never renders, which is most of what is left once the tokens have gone. Off by default, and the one pruning
  option that cannot be proven from the build.
- [Spec files](/docs/theming/spec) – generate a machine-readable description of your design system.
- [MCP server](/docs/ai/mcp-server) and [llms.txt](/docs/ai/llms-txt) – expose your design system to AI tooling.

## Getting help

If something does not carry over, check the [open issues](https://github.com/gajus/bamboocss/issues) first. If you
cannot find it, please open [a new issue](https://github.com/gajus/bamboocss/issues/new/choose) with a minimal
reproduction.
