> 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/paint-format.md).

# Paint Format

Copy formatting from one cell or range and apply it to another

The Paint Format feature, also known as Format Painter, allows users to copy formatting from one cell or range and apply it to another location. This is useful for quickly applying consistent formatting across your spreadsheet.

## Overview

Paint Format copies all visual formatting from the source cells (colors, borders, fonts, alignment, number formats, etc.) and applies it to target cells, without affecting the cell values.

## Basic Usage

### Using the Toolbar Button

The easiest way to use Paint Format is through the `ButtonPaintFormat` toolbar button:

```tsx
import {
  SpreadsheetProvider,
  CanvasGrid,
  Toolbar,
  ButtonPaintFormat,
} from "@rowsncolumns/spreadsheet";
import { useSpreadsheetState } from "@rowsncolumns/spreadsheet-state";

function MySpreadsheet() {
  const {
    activeCell,
    activeSheetId,
    selections,
    onSavePaintFormat,
    isPaintFormatActive,
    // ... other hook values
  } = useSpreadsheetState({
    sheets,
    sheetData,
    onChangeSheets,
    onChangeSheetData,
  });

  return (
    <SpreadsheetProvider>
      <Toolbar>
        <ButtonPaintFormat
          isActive={isPaintFormatActive}
          onClick={() =>
            onSavePaintFormat(activeSheetId, activeCell, selections)
          }
        />
      </Toolbar>
      <CanvasGrid
        sheetId={activeSheetId}
        activeCell={activeCell}
        selections={selections}
        // ... other props
      />
    </SpreadsheetProvider>
  );
}
```

## How It Works

### 1. Copy Format (Activate Paint Format)

Click the Paint Format button or call `onSavePaintFormat` to copy formatting from the currently selected cells:

```tsx
// Save formatting from current selection
onSavePaintFormat(activeSheetId, activeCell, selections);
```

When paint format is active, `isPaintFormatActive` returns `true`, which can be used to show visual feedback.

### 2. Apply Format

Once activated, simply select the target cells where you want to apply the formatting. The spreadsheet automatically applies the copied formatting to the new selection.

### 3. Deactivate

Paint format automatically deactivates after applying to one selection. To cancel without applying, click the Paint Format button again.

## Programmatic Usage

You can also use paint format programmatically:

```tsx
import { useSpreadsheetState } from "@rowsncolumns/spreadsheet-state";

const {
  onSavePaintFormat,
  onApplyPaintFormat,
  isPaintFormatActive,
  paintFormat, // Direct function to paint from one range to another
} = useSpreadsheetState({
  // ... configuration
});

// Copy formatting from range A1:B2
onSavePaintFormat(
  1, // sheetId
  { rowIndex: 1, columnIndex: 1 }, // activeCell
  [{
    range: {
      startRowIndex: 1,
      endRowIndex: 2,
      startColumnIndex: 1,
      endColumnIndex: 2,
    }
  }]
);

// Or paint directly from one range to another
paintFormat(
  {
    startRowIndex: 1,
    endRowIndex: 2,
    startColumnIndex: 1,
    endColumnIndex: 2,
    sheetId: 1,
  },
  {
    startRowIndex: 5,
    endRowIndex: 6,
    startColumnIndex: 3,
    endColumnIndex: 4,
    sheetId: 1,
  }
);
```

## What Gets Copied

Paint Format copies the following formatting properties:

* **Text formatting**: Font family, font size, bold, italic, underline, strikethrough
* **Colors**: Text color, background color
* **Borders**: All border styles and colors
* **Alignment**: Horizontal and vertical alignment
* **Number formats**: Currency, percentage, date, custom formats
* **Text wrapping**: Wrap, overflow, or clip
* **Indentation**: Text indentation levels
* **Merged cells**: the merge configuration of the source block travels with the pattern and is tiled onto the target with the pattern's period (a merge that a partial trailing tile would push past the target's edge is dropped). The target's own merges inside the painted range are replaced. Merging keeps only the upper-left value of each merged block, exactly as merging manually does.
* **Table banding / alternating colours**: when the source sits inside a table (or an alternating colours range), the stripe colours the grid shows are copied as static cell fills — the target does not become a table, matching Google Sheets.

## What Does NOT Get Copied

Paint Format does NOT copy:

* Cell values or formulas
* Cell comments/notes
* Data validation rules
* Conditional formatting rules
* Protected range settings
* Merges that reach outside the source selection (only merges fully inside the copied block are part of the pattern); a multi-area source copies formats only

## Complete Example

```tsx
import React, { useState } from "react";
import {
  SpreadsheetProvider,
  CanvasGrid,
  Toolbar,
  ButtonPaintFormat,
  ToolbarSeparator,
  ButtonBold,
  ButtonItalic,
  BackgroundColorSelector,
} from "@rowsncolumns/spreadsheet";
import {
  useSpreadsheetState,
  type SheetData,
} from "@rowsncolumns/spreadsheet-state";

function SpreadsheetWithPaintFormat() {
  const [sheets, setSheets] = useState([
    { sheetId: 1, rowCount: 100, columnCount: 26, title: "Sheet 1" }
  ]);
  const [sheetData, setSheetData] = useState<SheetData>({});

  const {
    activeCell,
    activeSheetId,
    selections,
    getCellData,
    getEffectiveFormat,
    onChangeActiveCell,
    onChangeSelections,
    onChangeFormatting,
    onSavePaintFormat,
    isPaintFormatActive,
  } = useSpreadsheetState({
    sheets,
    sheetData,
    onChangeSheets: setSheets,
    onChangeSheetData: setSheetData,
  });

  const currentCellFormat = getEffectiveFormat(
    activeSheetId,
    activeCell.rowIndex,
    activeCell.columnIndex
  );

  return (
    <SpreadsheetProvider>
      <Toolbar>
        <ButtonBold
          isActive={currentCellFormat?.textFormat?.bold}
          onClick={() =>
            onChangeFormatting(
              activeSheetId,
              activeCell,
              selections,
              "textFormat",
              { bold: !currentCellFormat?.textFormat?.bold }
            )
          }
        />
        <ButtonItalic
          isActive={currentCellFormat?.textFormat?.italic}
          onClick={() =>
            onChangeFormatting(
              activeSheetId,
              activeCell,
              selections,
              "textFormat",
              { italic: !currentCellFormat?.textFormat?.italic }
            )
          }
        />
        <BackgroundColorSelector
          color={currentCellFormat?.backgroundColor}
          onChange={(color) =>
            onChangeFormatting(
              activeSheetId,
              activeCell,
              selections,
              "backgroundColor",
              color
            )
          }
        />
        <ToolbarSeparator />
        <ButtonPaintFormat
          isActive={isPaintFormatActive}
          onClick={() =>
            onSavePaintFormat(activeSheetId, activeCell, selections)
          }
        />
      </Toolbar>
      <CanvasGrid
        sheetId={activeSheetId}
        activeCell={activeCell}
        selections={selections}
        getCellData={getCellData}
        onChangeActiveCell={onChangeActiveCell}
        onChangeSelections={onChangeSelections}
      />
    </SpreadsheetProvider>
  );
}
```

## Keyboard Shortcut

Paint Format can also be activated via keyboard shortcuts (implementation dependent on your configuration):

```tsx
// Example: Activate paint format with Ctrl+Shift+C (copy format)
// Then apply with Ctrl+Shift+V (paste format)
```

## Use Cases

### Consistent Table Headers

Apply the same header formatting to multiple tables:

1. Format one header cell (bold, background color, borders)
2. Click Paint Format
3. Select other header cells to apply the same style

### Standardizing Reports

Quickly apply consistent formatting across similar data sections:

1. Format one complete section
2. Use Paint Format to copy it
3. Apply to other sections

### Color Coding

Apply color coding patterns to categorize data:

1. Set up one cell with specific colors
2. Paint format to similar categories

## Best Practices

1. **Format once, apply many**: Create template cells with desired formatting, then use Paint Format to replicate
2. **Visual feedback**: The `isPaintFormatActive` state should be used to provide visual indication that paint format mode is active
3. **Undo support**: Paint format operations are tracked in undo history
4. **Performance**: Paint format is efficient even for large ranges

## API Reference

### useSpreadsheetState Returns

| Property              | Type                                        | Description                                  |
| --------------------- | ------------------------------------------- | -------------------------------------------- |
| `onSavePaintFormat`   | `(sheetId, activeCell, selections) => void` | Saves formatting from selection for painting |
| `onApplyPaintFormat`  | `(sheetId, activeCell, selections) => void` | Applies saved formatting to new selection    |
| `isPaintFormatActive` | `boolean`                                   | Whether paint format mode is active          |
| `paintFormat`         | `(sourceRange, targetRange) => void`        | Directly paint from one range to another     |

## Limitations

* Paint format is a client-side operation and works with the current spreadsheet state
* Very large ranges may take a moment to process
* Painting merges onto a target that holds values keeps only the upper-left value of each merged block (the same rule as Merge cells)

## Troubleshooting

### Paint Format Not Working

Ensure you have the necessary callbacks:

```tsx
const {
  onSavePaintFormat,
  isPaintFormatActive,
  // These are required for paint format to work
  onChangeFormatting,
  getEffectiveFormat,
} = useSpreadsheetState({
  // configuration
});
```

### Visual Feedback Not Showing

Use `isPaintFormatActive` to style the button:

```tsx
<ButtonPaintFormat
  isActive={isPaintFormatActive}  // This provides visual feedback
  onClick={handlePaintFormat}
/>
```
