Skip to main content

Overview

The Table plugin provides a complete solution for creating and managing tables. It includes features for cell selection, merging, splitting, and formatting tabular data.

Installation

npm install @yoopta/table

Basic Usage

import { Table } from '@yoopta/table';

const plugins = [
  Table,
  // ... other plugins
];

<YooptaEditor
  editor={editor}
  plugins={plugins}
/>

Features

  • Flexible Tables: Create tables of any size
  • Cell Operations: Merge, split, insert, delete cells
  • Header Rows/Columns: Mark rows and columns as headers
  • Cell Alignment: Horizontal and vertical alignment
  • Column Resizing: Adjustable column widths
  • Cell Selection: Select multiple cells
  • Keyboard Shortcuts: Type table, ||, or 3x3 to insert

Structure

The Table plugin consists of nested elements:
table (props: headerRow, headerColumn, columnWidths)
└── table-row (multiple)
    └── table-data-cell (props: align, verticalAlign, colSpan, rowSpan, asHeader)

Configuration

import { Table } from '@yoopta/table';

const plugins = [
  Table,
];

Options

display
object
shortcuts
string[]
default:"['table', '||', '3x3']"
Keyboard shortcuts to trigger the plugin

Element Props

table

headerRow
boolean
default:"false"
Whether the first row should be treated as a header
headerColumn
boolean
default:"false"
Whether the first column should be treated as a header
columnWidths
number[]
default:"[200, 150, 250]"
Array of column widths in pixels

table-data-cell

asHeader
boolean
default:"false"
Whether this cell should be rendered as a header (th)
align
'left' | 'center' | 'right'
default:"left"
Horizontal text alignment
verticalAlign
'top' | 'middle' | 'bottom'
default:"top"
Vertical text alignment
colSpan
number
default:"1"
Number of columns this cell spans
rowSpan
number
default:"1"
Number of rows this cell spans
backgroundColor
string | null
Cell background color

Commands

buildTableElements

Creates the initial table structure.
import { TableCommands } from '@yoopta/table';

TableCommands.buildTableElements(editor, { rows: 3, columns: 3 });
rows
number
default:"3"
Number of rows to create
columns
number
default:"3"
Number of columns to create

insertRow

Insert a new row at a specific position.
TableCommands.insertRow(editor, blockId, {
  at: rowIndex,
  position: 'after', // or 'before'
});

insertColumn

Insert a new column at a specific position.
TableCommands.insertColumn(editor, blockId, {
  at: columnIndex,
  position: 'after', // or 'before'
});

deleteRow

Delete a specific row.
TableCommands.deleteRow(editor, blockId, rowIndex);

deleteColumn

Delete a specific column.
TableCommands.deleteColumn(editor, blockId, columnIndex);

mergeCells

Merge selected cells into one.
TableCommands.mergeCells(editor, blockId, {
  startRow: 0,
  startCol: 0,
  endRow: 1,
  endCol: 1,
});

splitCell

Split a merged cell.
TableCommands.splitCell(editor, blockId, {
  row: rowIndex,
  col: columnIndex,
});

updateCellProps

Update cell properties.
TableCommands.updateCellProps(editor, blockId, {
  row: rowIndex,
  col: columnIndex,
  props: {
    align: 'center',
    backgroundColor: '#f3f4f6',
  },
});

clearContents

Clear contents of selected cells.
import { clearContents } from '@yoopta/table';

clearContents(editor, blockId, selectedCells);

Initial Structure

When created, the plugin initializes with a 3x3 table:
{
  table: {
    headerRow: false,
    headerColumn: false,
    columnWidths: [200, 150, 250],
  },
  rows: [
    // 3 rows
    {
      cells: [
        // 3 cells per row
        {
          align: 'left',
          verticalAlign: 'top',
          colSpan: 1,
          rowSpan: 1,
        },
      ],
    },
  ],
}

Custom Rendering

import { Table } from '@yoopta/table';

const CustomTable = Table.extend({
  elements: {
    table: {
      render: (props) => {
        const { headerRow, headerColumn, columnWidths } = props.element.props;
        
        return (
          <div className="table-wrapper">
            <table
              className="custom-table"
              data-header-row={headerRow}
              data-header-col={headerColumn}
              {...props.attributes}
            >
              <tbody>{props.children}</tbody>
            </table>
          </div>
        );
      },
    },
    'table-row': {
      render: (props) => (
        <tr className="table-row" {...props.attributes}>
          {props.children}
        </tr>
      ),
    },
    'table-data-cell': {
      render: (props) => {
        const {
          asHeader,
          align,
          verticalAlign,
          colSpan,
          rowSpan,
          backgroundColor,
        } = props.element.props;
        
        const CellTag = asHeader ? 'th' : 'td';
        
        return (
          <CellTag
            colSpan={colSpan}
            rowSpan={rowSpan}
            align={align}
            valign={verticalAlign}
            style={{ backgroundColor }}
            {...props.attributes}
          >
            {props.children}
          </CellTag>
        );
      },
    },
  },
});

Parsers

HTML Deserialization

The plugin automatically deserializes <table> tags:
<table>
  <tbody>
    <tr>
      <td>Cell 1</td>
      <td>Cell 2</td>
    </tr>
    <tr>
      <td>Cell 3</td>
      <td>Cell 4</td>
    </tr>
  </tbody>
</table>

HTML Serialization

<table>
  <tbody>
    <tr>
      <td align="left" valign="top">Cell content</td>
    </tr>
  </tbody>
</table>

Markdown Serialization

| Column 1 | Column 2 | Column 3 |
|----------|----------|----------|
| Cell 1   | Cell 2   | Cell 3   |
| Cell 4   | Cell 5   | Cell 6   |

Keyboard Behavior

The Table plugin has special keyboard handling:
  • Tab: Move to next cell
  • Shift+Tab: Move to previous cell
  • Arrow Keys: Navigate between cells
  • Enter: Move to cell below
  • Backspace: Delete cell content (not the cell)

Cell Selection

The plugin supports multi-cell selection:
// Access selected cells
import {
  TABLE_CELLS_IN_SELECTION,
  TABLE_SLATE_TO_SELECTION_SET,
} from '@yoopta/table';

const selectedCells = TABLE_CELLS_IN_SELECTION.get(slate);
const selectionSet = TABLE_SLATE_TO_SELECTION_SET.get(slate);

Use Cases

Data Tables

Display structured data and statistics

Pricing Tables

Compare pricing plans and features

Schedules

Create timetables and schedules

Comparison Tables

Compare products, services, or features

Best Practices

Always use header rows for better readability and accessibility
Avoid overly complex tables with too many merged cells
Consider mobile layouts for wide tables
Use consistent alignment patterns within columns
Provide table captions or descriptions for context

Styling Examples

Basic CSS

.table-wrapper {
  overflow-x: auto;
}

.custom-table {
  width: 100%;
  border-collapse: collapse;
}

.custom-table th,
.custom-table td {
  padding: 0.75rem;
  border: 1px solid #e5e7eb;
}

.custom-table th {
  background: #f9fafb;
  font-weight: 600;
  text-align: left;
}

.custom-table tr:hover td {
  background: #f9fafb;
}

Striped Rows

.custom-table tbody tr:nth-child(even) {
  background: #f9fafb;
}

Bordered Style

.custom-table {
  border: 2px solid #e5e7eb;
}

.custom-table th {
  border-bottom: 2px solid #e5e7eb;
}

Advanced Patterns

With Cell Selection Highlight

const SelectableTable = Table.extend({
  elements: {
    'table-data-cell': {
      render: (props) => {
        const isSelected = checkIfSelected(props.element);
        
        return (
          <td
            className={isSelected ? 'selected' : ''}
            {...props.attributes}
          >
            {props.children}
          </td>
        );
      },
    },
  },
});

With Sortable Columns

const SortableTable = Table.extend({
  elements: {
    'table-data-cell': {
      render: (props) => {
        if (props.element.props.asHeader) {
          return (
            <th {...props.attributes}>
              <button onClick={() => sortColumn(props.element)}>
                {props.children}
                <SortIcon />
              </button>
            </th>
          );
        }
        
        return <td {...props.attributes}>{props.children}</td>;
      },
    },
  },
});