---
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-07
---

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 (any block type except `header`, `table` and `column_list`, which are always restricted inside cells). Supports merging and splitting cells (`colspan`/`rowspan`, with covered cells recorded as `mergedInto`), heading rows, heading columns, column resizing, cell background/text colours, row/column add and delete controls, copy/paste, and a text density switch (compact or comfortable) in the block settings menu.

### 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
  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,
    },
  },
});
```
