# Tables in CKEditor 5 (overview)

<!-- AI-AGENT-NOTE: An interactive demo is embedded here but is not represented in this Markdown file. If you need to see it in action, open this page in a browser (e.g. via a browser-automation MCP like Chrome DevTools or Playwright), or let the user know a live demo is available on this page. -->

The table feature provides tools for creating and editing tables. Tables are great for organizing data in a clear, visually appealing way or creating structured content. They are also great for making document layouts for applications such as newsletters or email editors. There are two basic types of tables available: content tables, described in this feature guide, and [layout tables](layout-tables.md) used to organize the content rather than present tabular data. You can easily [switch between these two types](layout-tables.md#table-toggling).

<a id="demo">

## Demo

Use the insert table button to insert a new table into the content. Click inside the table to open a contextual toolbar. The toolbar lets you add or remove columns and rows . You can also merge or split cells .

Try toggling the caption on and off . You can also change the properties of the entire table or individual cells . To control the width of a column, click and drag its edge.

<!-- AI-AGENT-NOTE: An interactive demo is embedded here but is not represented in this Markdown file. If you need to see it in action, open this page in a browser (e.g. via a browser-automation MCP like Chrome DevTools or Playwright), or let the user know a live demo is available on this page. -->

You may look for more interesting details in the [Tables in CKEditor 5](https://ckeditor.com/blog/feature-of-the-month-tables-in-ckeditor-5/) blog post after reading this guide.

<a id="basic-table-features">

## Basic table features

The basic table features allow users to insert tables into content, add or remove columns and rows and merge or split cells.

The [`@ckeditor/ckeditor5-table`](https://www.npmjs.com/package/@ckeditor/ckeditor5-table) package contains multiple plugins that implement various table-related features. The [`Table`](../../api/module_table_table-Table.md) plugin is at the core of the ecosystem and it provides the table functionality. There are many other features that extend the editor capabilities:

<a id="table-selection">

## Table selection

The [`TableSelection`](../../api/module_table_tableselection-TableSelection.md) plugin introduces support for the custom selection system for tables that lets you:

* Select an arbitrary rectangular table fragment – a few cells from different rows, a column (or a few of them) or a row (or multiple rows).
* Apply formatting or add a link to all selected cells at once.

The table selection plugin is loaded automatically by the `Table` plugin and can be tested in the [demo above](#demo).

<a id="table-alignment">

## Table alignment

You can move tables horizontally to create a desired document layout. There are five alignment options in tables:

* Left (block) : A table aligns to the left. The table does not influence the flow of other content.
* Center : A table is horizontally centered, with equal amounts of space on both sides.
* Right (block) : A table is aligned to the right. The table does not influence the flow of other content.
* Left (wrap) : A table aligns to the left, and other content wraps around it.
* Right (wrap) : A table aligns to the right, and other content wraps around it.

You can find all of those options in the [table properties balloon](#table-contextual-toolbar). The balloon displays after you click on a table. Use the [demo above](#demo) to test all those options.

You can also control how the alignment appears in the editor’s output. Learn more about it in the [Table alignment in output](tables-installation.md#table-alignment-in-output) section.

<a id="table-cell-type">

## Table cell type

Two types of table cells can contain content: data cells (the default type) and header cells. Header cells provide semantic labelling for rows or columns, helping the browser or a screen reader understand the data arrangement and relations.

To change cell type, click the cell and use the cell properties icon from the table toolbar. Once there, choose the desired cell type.

To create a header row or column in your table, select all cells in the row or column and change their type to header. You will see the type change automatically in the row/column property dialog.

> **Tip**
>
> It is easier to use the row/column property dialog to change these into headers, rather than use the cell property method.

<a id="table-cell-scope">

## Table cell scope

Header cells can use the `scope` attribute to explicitly associate themselves with the related row or column, which helps screen readers understand the table structure.

This attribute is available only for header cells and is enabled by default. The editor assigns the `scope` attribute automatically based on a header cell’s position. If you prefer, you can change it manually in the cell properties balloon. The dropdown offers two explicit options:

* **Column header** – forces a header cell to describe the column and sets `scope="col"`.
* **Row header** – forces a header cell to describe the row and sets `scope="row"`.

To disable this behavior, set the [`config.table.tableCellProperties.scopedHeaders`](../../api/module_table_tableconfig-TableCellPropertiesConfig.md#member-scopedHeaders) configuration option to `false`.

<a id="typing-around-tables">

## Typing around tables

To type before or after a table easily, select the table, then press the Arrow key (`←` or `→`) once, depending on where you want to add content – before or after. The table is no longer selected and whatever text you type will appear in the desired position.

<a id="nesting-tables">

## Nesting tables

CKEditor 5 allows nesting tables inside other table’s cells. This may be used for creating advanced charts or layouts based on tables. The nested table can be formatted just like a regular one.

<a id="demo-2">

### Demo

You can test this feature in the demo below by adding a new table in the blank “abandoned” section at the bottom of the existing table. Click inside a cell and use the insert table button . A nested table will appear inside the cell.

<!-- AI-AGENT-NOTE: An interactive demo is embedded here but is not represented in this Markdown file. If you need to see it in action, open this page in a browser (e.g. via a browser-automation MCP like Chrome DevTools or Playwright), or let the user know a live demo is available on this page. -->

This demo presents a limited set of features. Visit the [feature-rich editor example](../../examples/builds-custom/full-featured-editor.md) to see more in action.

<a id="known-issues">

### Known issues

While table nesting is fully functional, the Markdown code generated with the [Markdown output](../autoformat.md) feature will not properly render nested tables ([#9475](https://github.com/ckeditor/ckeditor5/issues/9475)). Feel free to upvote 👍 this issue on GitHub if it is important for you.

<a id="table-contextual-toolbar">

## Table contextual toolbar

The [`TableToolbar`](../../api/module_table_tabletoolbar-TableToolbar.md) plugin introduces a contextual toolbar for table. The toolbar appears when a table or a cell is selected and contains various table-related buttons. These would typically include add or remove columns and rows and merge or split cells . If these features are configured, the toolbar will also contain buttons for captions and table and cell properties.

The table selection plugin is loaded automatically by the `Table` plugin and can be tested in the [demo above](#demo). Learn more about configuring a contextual toolbar in the [Common API section](tables-installation.md#toolbars).

<a id="block-vs-inline-content-in-table-cells">

## Block vs inline content in table cells

The table feature allows for creating block content (like paragraphs, lists, headings, etc.) inside table cells. However, if a table cell contains just one paragraph and this paragraph has no special attributes (like text alignment), the cell content is considered “inline” and the paragraph is not rendered.

This means that a table cell can have two states: with inline content or with block content. The reason for this differentiation is that most tables contain only inline content (like the [demo](#demo) above) and it is common for “data tables” to not contain any block content. In such a scenario, printing out `<p>` elements would be semantically wrong and also unnecessary. There are, however, scenarios where the user wants to create, for example, a list inside a table cell and then the support for block content is necessary.

> **Note**
>
> “Rendering” here refers to the view layer. In the model, a cell is always filled with at least a `<paragraph>`. This is because of consistency, as – since a cell always has some block content – the text is never directly inside the `<tableCell>`. This also allows features like `Enter` support to work out of the box (since a `<paragraph>` exists in the model, it can be split despite the fact that it is not present in the view).

<a id="inline-content">

### Inline content

The following is the model representation of table cells with inline content only (a single `<paragraph>` inside):

```html
<table>
	<tableRow>
		<tableCell>
			<paragraph>Foo</paragraph>
		</tableCell>
		<tableCell>
			<paragraph>Bar</paragraph>
		</tableCell>
	</tableRow>
</table>
```

The above model structure will be rendered to the [data](../../api/module_editor-classic_classiceditor-ClassicEditor.md#function-getData) as:

```html
<figure class="table">
	<table>
		<tbody>
			<tr>
				<td>Foo</td>
				<td>Bar</td>
			</tr>
		</tbody>
	</table>
</figure>
```

In the editing view (the editable container in which the user edits the content), additional `<span>` elements are created to compensate for the hidden `<paragraph>` elements:

```html
<figure class="table">
	<table>
		<tbody>
			<tr>
				<td><span>Foo</span></td>
				<td><span>Bar</span></td>
			</tr>
		</tbody>
	</table>
</figure>
```

<a id="block-content">

### Block content

If a table cell contains any other block content than a single `<paragraph>` with no attributes, these block elements will be rendered.

The following is a sample table with some block content (model representation):

```html
<table>
	<tableRow>
		<tableCell>
			<paragraph>Foo</paragraph>
			<paragraph>Bar</paragraph>
		</tableCell>
		<tableCell>
			<heading1>Some title</heading1>
		</tableCell>
		<tableCell>
			<paragraph textAlign="right">Baz</paragraph>
		</tableCell>
	</tableRow>
</table>
```

The above model structure will be rendered to the data and to the editing view as:

```html
<figure class="table">
	<table>
		<tbody>
			<tr>
				<td>
					<p>Foo</p>
					<p>Bar</p>
				</td>
				<td>
					<h2>Some title</h2>
				</td>
				<td>
					<p style="text-align: right;">Baz</p>
				</td>
			</tr>
		</tbody>
	</table>
</figure>
```

> **Note**
>
> At the moment, it is not possible to completely disallow block content in tables. See the [discussion on GitHub](https://github.com/ckeditor/ckeditor5-table/issues/101) about adding a configuration option that would enable that. Feel free to upvote 👍 if this feature is important to you.

<a id="contribute">

## Contribute

The source code of the feature is available on GitHub at <https://github.com/ckeditor/ckeditor5/tree/master/packages/ckeditor5-table>.

---

Full index of the CKEditor 5 documentation: [llms.txt](../../../llms.txt)
