Tables in CKEditor 5 (overview)

Contribute to this guide Show the table of contents

The table feature gives you tools to create and edit 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 table, described in this feature guide, and the layout tables used to organize the content rather, than present tabular data. You can easily switch between these two types.

Demo

Copy link

Use the insert table button Insert table 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 Table column and rows Table row. You can also merge or split cells Table cell.

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

The timeline of human spaceflight

The table below lists the first countries to send a human into space.

Country

Date of launch

Name

Spacecraft

USSR 1961 Yuri Gagarin Vostok-1
USA 1961 Alan Shepard Mercury-Redstone 3
Czechoslovakia 1978 Vladimír Remek Soyuz 28
Poland 1978 Mirosław Hermaszewski Soyuz 30
East Germany 1978 Sigmund Jähn Soyuz 31
Countries that sent their citizen into space

You may look for more interesting details in the Tables in CKEditor 5 blog post after reading this guide.

Basic table features

Copy link

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 package contains multiple plugins that implement various table-related features. The Table plugin is at the core of the ecosystem and it provides the table functionality. There are many other features that extend the editor capabilities:

Table selection

Copy link

The TableSelection 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.

Typing around tables

Copy link

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.

Nesting tables

Copy link

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.

Demo

Copy link

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 Insert table. A nested table will appear inside the cell.

American spacecraft

Status Name and service time
discontinued
Mercury 1961-1963
Gemini 1965-1966
Apollo 1968-1975
Space shuttle 1981-2011
in use
Crew Dragon since 2020
Boeing Starliner since 2024
planned
Orion planned 2025
abandoned

Known issues

Copy link

While table nesting is fully functional, the Markdown code generated with the Markdown output feature will not properly render nested tables (#9475). Feel free to upvote 👍  this issue on GitHub if it is important for you.

Table contextual toolbar

Copy link

The TableToolbar 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 Table column and rows Table row and merge or split cells Table cell. If these features are configured, the toolbar will also contain buttons for captions Table caption and table Table properties and cell Cell properties properties.

An extended contextual toolbar.

The table selection plugin is loaded automatically by the Table plugin and can be tested in the demo above. Learn more about configuring a contextual toolbar in the Common API section below.

Block vs inline content in table cells

Copy link

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 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).

Inline content

Copy link

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

<table>
    <tableRow>
        <tableCell>
            <paragraph>Foo</paragraph>
        </tableCell>
        <tableCell>
            <paragraph>Bar</paragraph>
        </tableCell>
    </tableRow>
</table>
Copy code

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

<figure class="table">
    <table>
        <tbody>
            <tr>
                <td>Foo</td>
                <td>Bar</td>
            </tr>
        </tbody>
    </table>
</figure>
Copy code

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:

<figure class="table">
    <table>
        <tbody>
            <tr>
                <td><span>Foo</span></td>
                <td><span>Bar</span></td>
            </tr>
        </tbody>
    </table>
</figure>
Copy code

Block content

Copy link

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):

<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>
Copy code

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

<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>
Copy code
Note

At the moment, it is not possible to completely disallow block content in tables. See the discussion on GitHub about adding a configuration option that would enable that. Feel free to upvote 👍  if this feature is important to you.

Installation

Copy link

After installing the editor, add the feature to your plugin list and toolbar configuration:

import { ClassicEditor, Table, TableToolbar } from 'ckeditor5';

ClassicEditor
	.create( document.querySelector( '#editor' ), {
		licenseKey: '&lt;YOUR_LICENSE_KEY&gt;', // Or 'GPL'.
		plugins: [ Table, TableToolbar, Bold, /* ... */ ],
		toolbar: [ 'insertTable', /* ... */ ],
		table: {
			// Configuration.
		}
	} )
	.then( /* ... */ )
	.catch( /* ... */ );
Copy code

Table contextual toolbar

Copy link

Easily control your tables employing a dedicated toolbar.

ClassicEditor
    .create( document.querySelector( '#editor' ), {
        // ... Other configuration options ...
        table: {
            contentToolbar: [ 'tableColumn', 'tableRow', 'mergeTableCells' ]
        }
    } )
    .then( /* ... */ )
    .catch( /* ... */ );
Copy code

Default table headers

Copy link

To make every inserted table have n number of rows and columns as table headers by default, set an optional table configuration property defaultHeadings as follows:

ClassicEditor
    .create( document.querySelector( '#editor' ), {
        // ... Other configuration options ...
        table: {
            defaultHeadings: { rows: 1, columns: 1 }
        }
    } )
    .then( /* ... */ )
    .catch( /* ... */ );
Copy code

Check the table with default headers applied to both the first row and the first column in the demo below. Click on the table and use the column properties Table column or the row properties Table row UI button to toggle the respective headers.

Financial report

Main product line for Q4.

 

Sales

Income

Revenue

Walker

1050

$104.000

15%

Stroller

24

$12.000

10%

Walker 3

980

$97.000

15%

Disallow nesting tables

Copy link

By default, the editor allows nesting a table inside another table’s cell.

To disallow nesting tables, you need to register an additional schema rule. It needs to be added before the data is loaded into the editor. Due to that, it is best to implement it as a plugin:

function DisallowNestingTables( editor ) {
    editor.model.schema.addChildCheck( ( context, childDefinition ) => {
        if ( childDefinition.name == 'table' && Array.from( context.getNames() ).includes( 'table' ) ) {
            return false;
        }
    } );
}

// Pass it via config.extraPlugins or config.plugins:

ClassicEditor
    .create( document.querySelector( '#editor' ), {
        // ... Other configuration options ...
        extraPlugins: [ DisallowNestingTables ],
    } )
    .then( /* ... */ )
    .catch( /* ... */ );
Copy code
Note

Check the step-by-step tutorial if you need more information about the technical side of this solution.

Note

We recommend using the official CKEditor 5 inspector for development and debugging. It will give you tons of useful information about the state of the editor such as internal data structures, selection, commands, and many more.

Common API

Copy link

UI components

Copy link

The Table plugins register the following UI components:

Component name Registered by
The 'insertTable' dropdown Table
The 'tableColumn' dropdown
The 'tableRow' dropdown
The 'mergeTableCells' split button

Toolbars

Copy link

The TableToolbar plugin introduces two balloon toolbars for tables.

  • The content toolbar shows up when a table cell is selected and it is anchored to the table. It is possible to configure its content. Normally, the toolbar contains the table-related tools such as 'tableColumn' and 'tableRow' dropdowns and 'mergeTableCells' split button.
  • The table toolbar shows up when the whole table is selected, for instance using the widget handler. It is possible to configure its content.

Editor commands

Copy link
Command name Command class Belongs to (top–level plugin)
'insertTable' InsertTableCommand Table
'insertTableColumnLeft' InsertColumnCommand
'insertTableColumnRight' InsertColumnCommand
'insertTableRowAbove' InsertRowCommand
'insertTableRowBelow' InsertRowCommand
'removeTableColumn' RemoveColumnCommand
'removeTableRow' RemoveRowCommand
'selectTableColumn' SelectColumnCommand
'selectTableRow' SelectRowCommand
'setTableColumnHeader' SetHeaderColumnCommand
'setTableRowHeader' SetHeaderRowCommand
'mergeTableCellRight' MergeCellCommand
'mergeTableCellLeft' MergeCellCommand
'mergeTableCellUp' MergeCellCommand
'mergeTableCellDown' MergeCellCommand
'splitTableCellVertically' SplitCellCommand
'splitTableCellHorizontally' SplitCellCommand
Note

We recommend using the official CKEditor 5 inspector for development and debugging. It will give you tons of useful information about the state of the editor such as internal data structures, selection, commands, and many more.

Contribute

Copy link

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