JavaScript Data GridColor Picker Cell Type - Step-by-Step Guide (React)

Overview

This guide shows how to create a color picker editor cell using react-colorful's HexColorPicker with React's EditorComponent. Perfect for selecting colors in design tools, theme builders, or any scenario where users need to choose hex color values.

Difficulty: Beginner Time: ~15 minutes Libraries: react-colorful

What You'll Build

A cell that:

  • Displays an input showing the hex value and a popover with a color picker when editing
  • Shows the selected color as a swatch when viewing (custom renderer)
  • Stores values as hex color strings (e.g., #FF5733)
  • Validates input so only valid hex values (e.g. #RRGGBB) are accepted
  • Works with React's component-based architecture
  • Uses hotRenderer to display color swatches in cells

Complete Example

Prerequisites

npm install @handsontable/react-wrapper react-colorful

What you need:

  • React 16.8+ (hooks support)
  • @handsontable/react-wrapper package
  • react-colorful package for the color picker UI
  • Basic React knowledge (hooks, JSX)

Step 1: Import Dependencies

import { HotTable, HotColumn, EditorComponent } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
import { rendererFactory } from 'handsontable/renderers';
import { HexColorPicker } from 'react-colorful';

registerAllModules();

What we're importing:

  • EditorComponent - React component for creating custom editors
  • HotTable and HotColumn - React wrapper components
  • rendererFactory - For the custom cell renderer (color swatch)
  • HexColorPicker - Lightweight color picker from react-colorful

Step 2: Create the Editor Component

Create a React component that uses EditorComponent with the render prop pattern. The editor shows an input with the current hex value and a popover containing the color picker.

export const ColorPickerEditor = () => {
  return (
    <EditorComponent>
      {({ value, setValue }) => (
        <div className="color-picker-editor">
          <input className="color-picker-editor-input" value={value} readOnly />
          <div className="color-picker-editor-popover">
            <HexColorPicker color={value || '#000000'} onChange={(color) => setValue(color)} />
          </div>
        </div>
      )}
    </EditorComponent>
  );
};

What's happening:

  1. EditorComponent wraps your editor UI
  2. The children prop is a function that receives editor state
  3. value - Current cell value (hex color string)
  4. setValue - Function to update the value
  5. The input displays the current hex value; the popover shows HexColorPicker for picking a color
  6. When the user selects a color, setValue(color) updates the value; the editor closes on blur (e.g. clicking outside)

Key concepts:

  • Render prop pattern: EditorComponent uses a function as children
  • Controlled component: HexColorPicker receives color and onChange
  • Input + popover: The input shows the hex code; the popover is positioned below and contains the picker

Step 3: Add Styling

Style the cell (swatch display), the editor container, the input, and the popover. The popover is positioned below the editor so the color picker appears when the cell is in edit mode.

.color-picker-cell {
  display: flex;
  align-items: center;
  justify-content: center;
}

.color-picker-swatch {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  flex-shrink: 0;
  border: 1px solid rgba(0, 0, 0, 0.15);
}

.color-picker-editor {
  width: 100%;
  height: 100%;
  box-sizing: border-box !important;
  border: none;
  border-radius: 0;
  outline: none;
}

.color-picker-editor-popover {
  position: absolute;
  top: 100%;
  left: 0;
  padding-top: 8px;
}

.color-picker-editor:focus {
  outline: none;
}

.color-picker-editor-input {
  width: 100%;
  height: 100%;
  box-sizing: border-box !important;
  border: none;
  border-radius: 0;
  outline: none;
}

What's happening:

  • .color-picker-cell and .color-picker-swatch display a circular color swatch in the cell
  • .color-picker-editor is the editor wrapper; .color-picker-editor-input styles the hex value input
  • .color-picker-editor-popover positions the color picker below the editor (e.g. top: 100%, left: 0)
  • HexColorPicker has built-in styling from react-colorful

Step 4: Prepare Sample Data

Use an array of row objects and add a color property with random hex values. The example uses an inventory-style dataset (with columns like id, itemName, itemNo, cost, valueStock, and optionally more); you can use any structure as long as each row has a color field.

const inputData = [
  { id: 640329, itemName: 'Lunar Core', itemNo: 'XJ-12', cost: 350000, valueStock: 700000 },
  { id: 863104, itemName: 'Zero Thrusters', itemNo: 'QL-54', cost: 450000, valueStock: 0 },
  // ... more rows
];

export const data = inputData.map((el) => ({
  ...el,
  color: `#${
    Math.round(0x1000000 + 0xffffff * Math.random())
      .toString(16)
      .slice(1)
      .toUpperCase()
  }`,
}));

What's happening:

  • Each row is an object with columns such as id, itemName, itemNo, cost, valueStock
  • A color property is added with a random hex value in #RRGGBB format

Step 5: Use in Handsontable

Use the editor, custom renderer, and validator on the color column. The example table has multiple columns; the color column uses hotRenderer and validator.

const colorCellRenderer = rendererFactory(({ td, value }) => {
  td.innerHTML = `<span class="color-picker-cell"><span class="color-picker-swatch" style="background:${value}"></span></span>`;
});

const colorValidator = (value, callback) => {
  callback(value.length === 7 && value[0] === '#'); // validate hex format
};

const ExampleComponent = () => {
  return (
    <HotTable
      data={data}
      colHeaders={['ID', 'Item Name', 'Item Color', 'Item No.', 'Cost', 'Value in Stock']}
      autoRowSize={true}
      rowHeaders={true}
      height="auto"
      width="100%"
      licenseKey="non-commercial-and-evaluation"
    >
      <HotColumn data="id" type="numeric" width={80} className="htLeft" />
      <HotColumn data="itemName" type="text" width={200} className="htLeft" />
      <HotColumn
        data="color"
        width={120}
        editor={ColorPickerEditor}
        hotRenderer={colorCellRenderer}
        validator={colorValidator}
        className="htLeft"
      />
      <HotColumn data="itemNo" type="text" width={100} className="htLeft" />
      <HotColumn data="cost" type="numeric" width={70} className="htLeft" />
      <HotColumn data="valueStock" type="numeric" width={130} className="htRight" />
    </HotTable>
  );
};

What's happening:

  • editor={ColorPickerEditor} - Assigns the color picker editor to the column
  • hotRenderer={colorCellRenderer} - Renders a color swatch in the cell instead of the raw hex string
  • validator={colorValidator} - Ensures only valid hex values (e.g. #RRGGBB) are accepted
  • data="color" - Binds to the color property in each row; double-click opens the picker

Key features:

  • Swatch display via custom renderer, validation, and type-safe editor with TypeScript

How It Works - Complete Flow

  1. Initial Render: Cell shows a color swatch (custom renderer); value is stored as hex (e.g. #FF5733)
  2. User Double-Clicks or Enter: Editor opens
  3. Editor Opens: EditorComponent positions container over cell; input shows current hex value; popover with HexColorPicker appears below
  4. User Interaction: Drag sliders or click palette → setValue(color) updates the value; input and picker stay in sync
  5. Close: User blurs the editor (e.g. clicks outside) → value is saved and editor closes
  6. Validation: colorValidator ensures value is valid hex before saving
  7. Save: Hex value saved to cell; cell renderer shows the new swatch

Enhancements

1. Custom Renderer with Color Swatch (and Optional Hex Label)

The example uses hotRenderer to show only a circular swatch. You can extend it to show the hex code next to the swatch:

import { rendererFactory } from 'handsontable/renderers';

// Swatch only (as in the example)
const colorCellRenderer = rendererFactory(({ td, value }) => {
  td.innerHTML = `<span class="color-picker-cell"><span class="color-picker-swatch" style="background:${value}"></span></span>`;
});

// Or swatch + hex label
const colorRendererWithHex = rendererFactory(({ td, value }) => {
  const hexColor = value || '#cccccc';
  td.innerHTML = `
    <div style="display: flex; align-items: center; gap: 8px; padding: 4px;">
      <div style="width: 24px; height: 24px; background: ${hexColor}; border: 1px solid #ddd; border-radius: 4px;"></div>
      <span style="font-family: monospace; font-size: 12px;">${hexColor}</span>
    </div>
  `;
});

// Use hotRenderer in HotColumn
<HotColumn
  data="color"
  editor={ColorPickerEditor}
  hotRenderer={colorCellRenderer}
  validator={colorValidator}
/>

What's happening:

  • hotRenderer is used in the React wrapper for custom cell rendering
  • Swatch-only keeps the cell compact; adding the hex label improves readability when needed

2. Explicit "Apply" Button (Optional)

The example closes the editor on blur. If you prefer explicit confirmation, add a button and use finishEditing from the render props:

<EditorComponent>
  {({ value, setValue, finishEditing }) => (
    <div className="color-picker-editor">
      <input className="color-picker-editor-input" value={value} readOnly />
      <div className="color-picker-editor-popover">
        <HexColorPicker color={value || '#000000'} onChange={(color) => setValue(color)} />
      </div>
      <button className="button" type="button" onClick={() => finishEditing()}>
        Apply Colour
      </button>
    </div>
  )}
</EditorComponent>

3. Using External CSS File

The example uses a separate CSS file for cell and editor styles (e.g. example1.css). Import it in your component:

import './example1.css'; // or your color-picker-editor.css

const ColorPickerEditor = () => {
  // ... component code
};

Keep the .color-picker-cell, .color-picker-swatch, .color-picker-editor, .color-picker-editor-input, and .color-picker-editor-popover rules so the swatch and the editor look correct.

4. RGBA or HSL Variants

react-colorful also provides RgbaColorPicker and HslColorPicker. For RGBA:

import { RgbaColorPicker } from 'react-colorful';

// Note: value would need to be stored as rgba string or object
// and converted appropriately

5. Default Color

Handle empty cells with a sensible default:

<HexColorPicker
  color={value || '#000000'}
  onChange={(color) => setValue(color)}
/>

The || '#000000' ensures the picker always has a valid color.

Accessibility

The HexColorPicker from react-colorful provides keyboard support. For the editor input, you can add an aria-label so screen readers describe the field:

<input
  className="color-picker-editor-input"
  value={value}
  aria-label="Hex color value"
/>

Keyboard navigation:

  • Tab: Navigate to editor
  • Escape: Cancel editing
  • Click: Direct selection in color picker

Performance Considerations

Why This Is Fast

  1. react-colorful: Lightweight (~2KB gzipped), no heavy dependencies
  2. React Virtual DOM: Efficient updates only when value changes
  3. Controlled updates: onChange only fires when user interacts
  4. No unnecessary re-renders: Editor unmounts when closed

Bundle Size

react-colorful is tree-shakeable. If you only need hex:

import { HexColorPicker } from 'react-colorful';

TypeScript Support

EditorComponent is fully typed. For the color editor, the value is a string (hex). You can type the validator as well:

const colorValidator = (value: string, callback: (valid: boolean) => void) => {
  callback(value.length === 7 && value[0] === '#'); // validate color format
};

export const ColorPickerEditor = () => {
  return (
    <EditorComponent>
      {({ value, setValue }) => (
        <div className="color-picker-editor">
          <input className="color-picker-editor-input" value={value} readOnly />
          <div className="color-picker-editor-popover">
            <HexColorPicker color={value || '#000000'} onChange={(color) => setValue(color)} />
          </div>
        </div>
      )}
    </EditorComponent>
  );
};

Best Practices

  1. Provide fallback for empty values - Use value || '#000000' for HexColorPicker
  2. Use hotRenderer - Display color swatches in cells for better UX
  3. Store as hex - Hex strings are portable and easy to validate
  4. Input + popover - Showing the hex in an input with the picker in a popover keeps the cell compact and clear
  5. Optional "Apply" button - For explicit confirmation, add a button that calls finishEditing() from the render props

Congratulations! You've created a color picker editor using React's EditorComponent and react-colorful, perfect for color selection in your data grid!