# Vite

> Set up the React color picker in a Vite app: install it, import the stylesheet in main.tsx, and load only the picker modes your app actually uses.

Source: https://chroma-panel.jscrate.dev/react/frameworks/vite
Last updated: 2026-09-18

This page sets up the React color picker with Vite, from the install to a
working picker in `App.tsx`. There is no plugin to add and no configuration to
change. The package ships ESM, so Vite resolves it directly.

## Install

```bash
npm install chroma-panel
```

`react` and `react-dom` are peer dependencies and the package has no
dependencies of its own, so nothing else is pulled in.

## Import the stylesheet

The panel injects its stylesheet from an effect. In a client-rendered Vite app
that happens before you see anything, so the import is optional. Import it
yourself when you want the CSS in your build output rather than in a `<style>`
tag, for example under a strict Content Security Policy.

```tsx
// src/main.tsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";

import "chroma-panel/style.css";
import "./index.css";

import App from "./App";

const root = document.getElementById("root");

if (root !== null) {
  createRoot(root).render(
    <StrictMode>
      <App />
    </StrictMode>
  );
}
```

Once you import it yourself, pass `injectStyles={false}` so the panel does not
write the tag as well. It does not make your bundle smaller — the CSS is in the
module either way. The other ways to load the CSS are in
[theming](https://chroma-panel.jscrate.dev/react/handbook/theming#loading-the-css-yourself).

## A picker in App.tsx

```tsx
// src/App.tsx
import { useState } from "react";
import { ColorInput, type ColorChangeResult } from "chroma-panel";

export default function App() {
  const [color, setColor] = useState<string>("#3366cc");

  const handleChange = (result: ColorChangeResult): void => {
    setColor(result.hex);
  };

  return (
    <main>
      <ColorInput value={color} onChange={handleChange} injectStyles={false} />
      <p style={{ color }}>{color}</p>
    </main>
  );
}
```

`onChange` fires continuously while you drag. Use `onChangeComplete` for
anything that costs something, such as saving or a network call — see
[controlled and uncontrolled](https://chroma-panel.jscrate.dev/react/handbook/controlled#onchange-vs-onchangecomplete).

## If you use Tailwind

The package ships its rules inside `@layer chroma-panel`. An undeclared layer
sorts after Tailwind's, which would let the panel's rules beat your utility
classes. Import the stylesheet from your CSS file and declare the order there
instead of importing it in `main.tsx`:

```css
/* src/index.css */
@import "tailwindcss/theme.css" layer(theme);
@import "tailwindcss/preflight.css" layer(base);
@import "chroma-panel/style.css";
@import "tailwindcss/utilities.css" layer(utilities);
```

[Styling with Tailwind](https://chroma-panel.jscrate.dev/react/handbook/tailwind) explains why that order is the
one that works, and which class names each part of the panel takes.

## Only the modes you need

Importing `chroma-panel` registers all five modes: wheel, sliders, palettes,
image and pencils. Import the shell from `chroma-panel/panel` and add the modes
yourself to ship less.

```tsx
import { ChromaPanel } from "chroma-panel/panel";
import "chroma-panel/wheel";

<ChromaPanel modes={["wheel"]} />;
```

| What you import     | Added to your app |
| ------------------- | ----------------- |
| all five modes      | 22.2 kB           |
| shell plus one mode | 14.1 kB           |

Both figures are gzipped, with React external, measured as the increase in a
Vite production build.

> **Import modes for the side effect**
>
> A mode entry point registers itself when it loads. Write `import
> "chroma-panel/wheel"`, not a named import, or the bundler may drop it.

Every subpath, including `chroma-panel/core` for the color engine without React,
is listed under [entry points](https://chroma-panel.jscrate.dev/react/utils/entry-points).
