> For the complete documentation index, see [llms.txt](https://docs.rowsncolumns.app/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.rowsncolumns.app/configuration/features/custom-color-picker.md).

# Custom Color Picker

Every surface that renders a color picker accepts an optional `ColorSelector` prop, letting you replace the built-in picker with your own component. Use this to match your design system, integrate a third-party picker library, or restrict the palette users can choose from.

## Overview

The built-in `ColorSelector` (theme colors, standard colors, user-defined colors, hex input and eye dropper) is the default everywhere. Passing your own component to the `ColorSelector` prop swaps it at that surface:

| Component                 | Where the picker appears               |
| ------------------------- | -------------------------------------- |
| `TextColorSelector`       | Toolbar text color dropdown            |
| `BackgroundColorSelector` | Toolbar fill color dropdown            |
| `BorderSelector`          | Border color inside the border menu    |
| `BorderColorSelector`     | Standalone border color dropdown       |
| `SheetTabs`               | "Change color" in the tab context menu |
| `FloatingCellEditor`      | Text and fill color pickers            |

Because it is a plain prop, you can swap the picker on one surface and keep the default on the others.

## Writing a custom picker

Your component receives `ColorSelectorProps`:

```tsx
import type { ColorSelectorProps } from "@rowsncolumns/spreadsheet";

const BrandColorSelector = ({
  color,
  onChange,
  theme,
  resetButtonTitle = "Automatic",
  userDefinedColors,
  onAddUserDefinedColor,
}: ColorSelectorProps) => {
  const brandColors = ["#1E40AF", "#F59E0B", "#10B981", "#EF4444"];
  return (
    <div className="flex flex-col gap-1 p-2">
      <button onClick={() => onChange?.(undefined)}>{resetButtonTitle}</button>
      <div className="flex gap-1">
        {brandColors.map((value) => (
          <button
            key={value}
            aria-label={`Select ${value}`}
            style={{ backgroundColor: value, width: 24, height: 24 }}
            onClick={() => onChange?.(value)}
          />
        ))}
      </div>
    </div>
  );
};
```

`ColorSelectorProps`:

| Prop                    | Type                                  | Description                                                                                       |
| ----------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------- |
| `color`                 | `Color \| null`                       | Currently selected color                                                                          |
| `onChange`              | `(color: Color \| undefined) => void` | Call with the new color; `undefined` resets to automatic                                          |
| `theme`                 | `SpreadsheetTheme`                    | Active theme, for rendering theme color swatches                                                  |
| `resetButtonTitle`      | `string`                              | Label for the reset action ("Automatic", "No fill", "Reset")                                      |
| `userDefinedColors`     | `string[]`                            | Custom palette colors (see [User-Defined Colors](/configuration/features/user-defined-colors.md)) |
| `onAddUserDefinedColor` | `(color: string) => void`             | Add a color to the custom palette                                                                 |

## Usage in the toolbar

```tsx
import {
  Toolbar,
  TextColorSelector,
  BackgroundColorSelector,
  BorderSelector,
} from "@rowsncolumns/spreadsheet";

<Toolbar>
  <TextColorSelector
    color={currentCellFormat?.textFormat?.color}
    theme={theme}
    onChange={handleTextColorChange}
    ColorSelector={BrandColorSelector}
  />
  <BackgroundColorSelector
    color={currentCellFormat?.backgroundColor}
    theme={theme}
    onChange={handleBackgroundColorChange}
    ColorSelector={BrandColorSelector}
  />
  <BorderSelector
    borders={currentCellFormat?.borders}
    theme={theme}
    onChange={onChangeBorder}
    ColorSelector={BrandColorSelector}
  />
</Toolbar>
```

`BorderSelector` forwards the component to the border color picker inside the borders menu.

## Sheet tab color picker

`SheetTabs` forwards the component to the "Change color" submenu of the tab context menu:

```tsx
import { SheetTabs } from "@rowsncolumns/spreadsheet";

<SheetTabs
  sheets={sheets}
  activeSheetId={activeSheetId}
  theme={theme}
  onChangeSheetTabColor={onChangeSheetTabColor}
  ColorSelector={BrandColorSelector}
/>
```

If you already supply a custom `ContextMenu` to `SheetTabs`, render your own picker inside it directly — the `ColorSelector` prop is forwarded to your context menu via `SheetTabContextMenuProps`.

## Floating cell editor

```tsx
import { FloatingCellEditor } from "@rowsncolumns/spreadsheet";

<FloatingCellEditor
  sheetId={activeSheetId}
  activeCell={activeCell}
  onChange={onChange}
  onChangeFormatting={onChangeFormatting}
  ColorSelector={BrandColorSelector}
/>
```

## Best practices

1. **Call `onChange` with `undefined`** for your reset action so "Automatic"/"No fill" behaves like the built-in picker
2. **Respect `resetButtonTitle`** — each surface passes the right label for its context
3. **Reuse one component** across surfaces for a consistent experience, or override only the surfaces you need
4. **Keep `userDefinedColors` support** if your app also uses the custom palette, so both features compose
