---
title: Using Vite
description: Easily use Bamboo with Vite, React and Typescript with our dedicated integration.
---

## Start a new project

<Steps>

### Create Vite project

To get started, we will need to create a new Vite project using `react-ts` template.

<Tabs items={['pnpm', 'npm', 'yarn', 'bun']}>
  {/* <!-- prettier-ignore-start --> */}
  <Tab>
    ```bash
    pnpm create vite test-app --template react-ts
    cd test-app
    pnpm install
    ```
  </Tab>
  <Tab>
    ```bash
    npm create vite@latest test-app -- --template react-ts
    cd test-app
    npm install
    ```
  </Tab>
  <Tab>
    ```bash
    yarn create vite test-app --template react-ts
    cd test-app
    yarn
    ```
  </Tab>
  <Tab>
    ```bash
    bun create vite test-app --template react-ts
    cd test-app
    bun install
    ```
  </Tab>
  {/* <!-- prettier-ignore-end --> */}
</Tabs>

### Install Bamboo

Install bamboo and the Vite plugin, then create your `bamboo.config.ts` file.

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

### Update package.json scripts

Add `"prepare": "bamboo codegen"` to `scripts`. It runs [codegen](/docs/references/cli#codegen) after every dependency
install, so the output directory can stay in `.gitignore`.

The build itself no longer needs it. The Vite plugin generates `styled-system/` in `buildStart`, before anything
resolves an import of it, so a fresh clone builds without a separate codegen step. Keep the script regardless: `tsc` and
your editor read the generated types without ever running the plugin.

### Configure the content

Make sure that all of the paths of your React components are included in the `include` section of the `bamboo.config.ts`
file.

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

export default defineConfig({
  // Whether to use css reset
  preflight: true,

  // Where to look for your css declarations
  include: ['./src/**/*.{js,jsx,ts,tsx}', './pages/**/*.{js,jsx,ts,tsx}'],

  // Files to exclude
  exclude: [],

  // The output directory for your css system
  outdir: 'styled-system',
})
```

### Add the plugin to your Vite config

```ts filename="vite.config.ts"
import { defineConfig } from 'vite'
import bamboocss from '@bamboocss/vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [bamboocss(), react()],
})
```

### Import the virtual stylesheet

```ts filename="src/main.tsx"
import 'virtual:bamboo.css'
```

Reference the ambient declaration so TypeScript accepts that import:

```ts filename="src/vite-env.d.ts"
/// <reference types="vite/client" />
/// <reference types="@bamboocss/vite/client" />
```

That is the whole setup – no `postcss.config.cjs` entry for Bamboo, and no file carrying the `@layer` statement, since
the virtual module emits its own. You still need PostCSS for anything else you run through it, autoprefixer included.

In development the stylesheet is served from memory and hot-replaced in place, so editing a style repaints without a
reload or a loss of component state. Editing `bamboo.config.ts`, or a preset it imports, restarts the server instead —
Bamboo declares it to Vite as a config file. A config edit can change what compiles and not only what a value resolves
to, so everything is rebuilt rather than half of it. In a production build the stylesheet is hashed into the asset graph
like any other CSS.

> **Note:** Feel free to remove `src/App.css` file as we don't need it anymore, and make sure to remove the import from
> the `src/App.tsx` file.

## Start your build process

Start the dev server with `pnpm dev`.

### Start using Bamboo

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

function App() {
  return <div className={css({ fontSize: '2xl', fontWeight: 'bold' })}>Hello 🎋!</div>
}

export default App
```

</Steps>

## Plugin options

`bamboocss()` compiles Bamboo style calls in development and production, and emits their globally shared declaration
atoms through the virtual stylesheet:

```tsx
// you write
export const title = css({ fontSize: 'lg', fontWeight: 'bold' })

// the bundle gets
export const title = 'fs_lg fw_bold'
```

| option            | default | what it does                                                     |
| ----------------- | ------- | ---------------------------------------------------------------- |
| `maxRecipeStates` | `65536` | Bound exact build-time enumeration for runtime recipe axes.      |
| `reportSummary`   | `true`  | Print a compiler coverage summary when a build finishes.         |
| `reportSkipped`   | `false` | Report every call site the compiler rejected, and why, per file. |
| `configPath`      | –       | Path to `bamboo.config.ts`, resolved the same way as the CLI.    |
| `cwd`             | –       | Directory the config is resolved from.                           |
| `pruneCss`        | `true`  | Remove rules for atoms no compiled module can emit. See below.   |

`pruneCss` drops every rule the source graph produced that nothing in the bundle can reach. It applies to builds only —
the dev server never prunes. Turning it off ships the whole extracted stylesheet: larger, and never wrong _by pruning_.
It also stands down the assertion that every compiled class has a rule, since that check exists to catch this pass
removing too much, so `pruneCss: false` is a true escape hatch — it cannot fail a build over reachability.

The pruned stylesheet is also renamed to a hash of its own bytes, and that is deliberately not a setting of its own.
`[hash]` is expanded before Bamboo prunes, so the name Vite assigned describes the sheet as it was _before_ pruning.
Leaving that name on pruned bytes is how a stale stylesheet outlives a deploy — a change to reachability alone, which is
what upgrading Bamboo is, leaves identical source CSS under an identical name with different content, and a CDN holding
that key keeps serving the old one. So the bytes and the name move together, or neither does.

Reach for `pruneCss: false` if something downstream derives an artifact from the stylesheet's _content_ during
`generateBundle` before Bamboo runs, or to rule pruning out while diagnosing a missing rule. Subresource integrity is
the clear case: an `integrity` attribute is a digest of the bytes, so no amount of reference rewriting can carry it
across an edit, and a browser handed a stale digest refuses the stylesheet outright. Note that this is a hazard of the
_pruning_, not of the rename — Bamboo rewrites the references it can see, and no rename happens unless the bytes already
moved.

Where the consumer can run after Bamboo instead — `order: 'post'` listed later, or `writeBundle`/`closeBundle` — prefer
that and keep the pruning. The build prints one line whenever pruning is off, so it is never off silently.

### Builds with more than one environment

An SSR framework builds a client bundle and a server bundle, and the stylesheet is emitted and finalized by the one that
imports it — usually the client, which finishes before the server environment has compiled anything. Pruning it there
would drop every rule for a class only the server graph reaches, so Bamboo prunes only once every environment the run
declared has been compiled, and names the ones outstanding when it does not:

```
🎋 info [vite] Reachability pruning skipped: the stylesheet is emitted by the "client"
   environment, and ssr has not been compiled in this run. The full extracted stylesheet
   ships — nothing is missing from it.
```

An environment a run declares and then never builds reads the same way, which is why the message says "not compiled in
this run" rather than "builds later" — that is the one case a reader cannot check for themselves.

Nothing to configure: `vite build` announces its environments, and so does any framework that sets `builder`. A script
driving `builder.build(environment)` itself does not, and Bamboo cannot tell the first environment it is not the last —
so it prunes, and fails the build if a later environment turns out to compile a class that was pruned. Call
`builder.buildApp()`, or set `builder: {}` in the Vite config, and the environments are known before the first one
builds.

### Seeing every diagnostic

Compiler errors list the first few findings and count the rest, which is right for reading one failure and wrong for
scoping a migration. Set `BAMBOO_DIAGNOSTIC_LIMIT` to a number, or to `all`, to see the whole list:

```bash
BAMBOO_DIAGNOSTIC_LIMIT=all vite build
```

Compilation and strict rejection are not options. A style-producing call that cannot be represented by the emitted CSS
fails instead of retaining a runtime fallback. See [Source compilation](/docs/guides/source-transformation) for the
accepted static and finite-dynamic shapes.

## Alternative: emit CSS from PostCSS

Bamboo can emit the stylesheet through PostCSS instead of the Vite plugin. Install `@bamboocss/dev` alone, run
`bamboo init --postcss` to write `postcss.config.cjs`, and add an entry CSS file carrying the layer order:

```css filename="src/index.css"
@layer reset, base, tokens, recipes, utilities;
```

Import that file from your root component in place of `virtual:bamboo.css`. This is a different integration: it has no
source compiler, so `css()` calls resolve at runtime and recipes retain their named extraction representation — which
means the generated style engine ships in your client bundle, where the plugin above compiles it away entirely.

Because that is invisible from the outside — both render the same — the PostCSS plugin says so when it finds a Vite
config. Declare the choice to silence it:

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

<Callout type="warning">
  Use this **or** `@bamboocss/vite`, not both. Each emits a complete stylesheet, so configuring the two together puts
  two copies of it in your bundle – and a minifier merges most of the duplication away, which makes it read as a
  rounding error rather than as the whole sheet twice.
</Callout>

## Troubleshooting

If your IDE does not autocomplete `styled-system` imports, add that directory to `include` in `tsconfig.json`. See
[the CLI guide](/docs/installation/cli#troubleshooting).
