---
title: "Table Block — Merged Cells, Paste from Excel"
description: "Tables with merged cells, per-cell rich content, and lossless paste from Excel, Google Docs, and Notion."
source: https://blokeditor.com/docs/table/
lastmod: 2026-09-28
---

Framework JavaScript

Block Tools Table

# Table block: merged cells and rich cell content

A full-featured table block. Each cell contains its own block editor. A cell accepts any block type except `header`, `table` and `column_list`, which are always restricted inside cells.

The tool supports merging and splitting cells. That uses `colspan`/`rowspan`, and covered cells are recorded as `mergedInto`.

- You also get heading rows, heading columns and column resizing.
- Cell background and text colours are supported too.
- So are row and column add and delete controls, and copy and paste.

The block settings menu holds a text density switch, either compact or comfortable.

### Import

TypeScript

```
import { Table } from '@bloklabs/core/tools';
```

### Configuration

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `rows` | `number` | `3` | Initial number of rows when a new table is inserted. |
| `cols` | `number` | `3` | Initial number of columns when a new table is inserted. |
| `withHeadings` | `boolean` | `false` | Whether the first row is styled as a heading row by default. |
| `withHeadingColumn` | `boolean` | `false` | Whether the first column is styled as a heading column by default. |
| `stretched` | `boolean` | `false` | When true, the table spans the full editor width by default. |
| `restrictedTools` | `string[]` | `[]` | Additional tool names to prevent from being inserted into table cells. |

### Save Data

TypeScript

```
interface TableData {
  withHeadings: boolean;       // First row is a heading row
  withHeadingColumn: boolean;  // First column is a heading column
  stretched?: boolean;
  content: CellContent[][];    // 2D array of cell content
  colWidths?: number[];        // Column widths in pixels
  initialColWidth?: number;    // Per-column width in px captured at creation; new columns get initialColWidth / 2
  textSize?: 'compact' | 'comfortable'; // Cell text density; omitted = 'compact' (small text)
}

// Each cell:
interface CellContent {
  blocks: string[];    // IDs of child blocks in this cell
  id?: string;         // Column id, the same on every cell of the column; minted on load when missing
  rowId?: string;      // Row id, the same on every cell of the row; minted on load when missing
  color?: string;      // Cell background colour
  textColor?: string;  // Cell text colour
  placement?: CellPlacement; // 9-way vertical+horizontal alignment (e.g. 'top-left', 'middle-center')
  colspan?: number;    // Columns this cell spans (origin cells only)
  rowspan?: number;    // Rows this cell spans (origin cells only)
  mergedInto?: [number, number]; // Set when covered by a merge; origin cell at [row, col]
}
```

JSON

```
{
  "id": "jkl012",
  "type": "table",
  "data": {
    "withHeadings": true,
    "withHeadingColumn": false,
    "content": [
      [{ "blocks": ["block1"] }, { "blocks": ["block2"] }],
      [{ "blocks": ["block3"] }, { "blocks": ["block4"] }]
    ]
  }
}
```

### Usage Example

TypeScript

```
import { Blok } from '@bloklabs/core';
import { Table } from '@bloklabs/core/tools';

const editor = new Blok({
  holder: 'editor',
  tools: {
    table: {
      class: Table,
      rows: 3,
      cols: 3,
      withHeadings: true,
    },
  },
});
```
