How to Configure Table Features (Merge Cells & Resize Columns) in reactjs-tiptap-editor

Enable resizable: true in the Table extension options to activate column resizing, and invoke editor.chain().focus().mergeCells().run() via the RichTextBubbleTable component to merge selected cells.

The reactjs-tiptap-editor repository extends TipTap's @tiptap/extension-table with custom React wrappers to provide a comprehensive table editing experience. Configuring table features requires modifying the extension configuration in src/extensions/Table/Table.ts for resizing capabilities, and leveraging the bubble menu component in src/components/Bubble/RichTextBubbleTable.tsx for cell merging operations.

Enabling Column Resizing

Extension Configuration

Column resizing is controlled through the custom Table extension located at src/extensions/Table/Table.ts. The extension overrides the default addOptions() method to enable resizing behaviors by default.

// src/extensions/Table/Table.ts
addOptions() {
  return {
    ...this.parent?.(),
    resizable: true,          // Enables draggable column borders
    lastColumnResizable: true // Allows resizing the right-most column
  };
}

When resizable is set to true, TipTap injects mouse-draggable handles on each column border. Dragging these handles updates the column widths in the document model immediately.

Runtime Configuration

You can override these defaults when initializing the editor instance to disable resizing for specific use cases, such as read-only views.

import { Table } from '@/extensions/Table/Table';

const editor = new Editor({
  extensions: [
    Table.configure({
      resizable: false,
      lastColumnResizable: false
    }),
    // ...other extensions
  ],
});

Implementing Cell Merging

RichTextBubbleTable Integration

The merge functionality is exposed through the RichTextBubbleTable component in src/components/Bubble/RichTextBubbleTable.tsx. This bubble menu appears when the cursor is inside a table and provides the merge action.

The component calls onMergeCell (lines 51-53) which executes the merge command:

// src/components/Bubble/RichTextBubbleTable.tsx
function onMergeCell() {
  editor.chain().focus().mergeCells().run();
}

The button is conditionally enabled only when editor?.can()?.mergeCells?.() returns true, ensuring the action only appears for valid rectangular cell selections.

Programmatic Usage

You can trigger cell merging from any custom UI component using the same chain command:

const mergeSelectedCells = () => {
  editor.chain().focus().mergeCells().run();
};

// Example action button with disabled state
<ActionButton
  action={mergeSelectedCells}
  disabled={!editor?.can()?.mergeCells?.()}
  icon='TableCellsMerge'
  tooltip='Merge selected cells'
/>

Complete Implementation Example

Below is a minimal implementation that demonstrates inserting a resizable table and accessing the merge functionality:

import React from 'react';
import { ActionButton } from '@/components';
import { Table } from '@/extensions/Table/Table';
import { RichTextBubbleTable } from '@/components/Bubble/RichTextBubbleTable';
import { useEditorInstance } from '@/store/editor';

export const TableEditor = () => {
  const editor = useEditorInstance();

  const insertTable = () => {
    editor
      .chain()
      .focus()
      .insertTable({ rows: 3, cols: 3, withHeaderRow: false })
      .run();
  };

  return (
    <>
      <ActionButton
        action={insertTable}
        icon='Table'
        tooltip='Insert 3×3 resizable table'
      />
      {/* Bubble menu provides merge, split, add/delete row/column actions */}
      <RichTextBubbleTable />
    </>
  );
};

This configuration creates tables with draggable column borders (via the resizable option) and exposes the merge cells button through the bubble menu when multiple cells are selected.

Summary

  • Configure resizable: true in src/extensions/Table/Table.ts to enable draggable column resize handles on all tables.
  • Set lastColumnResizable: true to ensure users can resize the right-most column border.
  • Invoke editor.chain().focus().mergeCells().run() to programmatically merge selected cells into a single cell.
  • Use editor.can().mergeCells() to conditionally render merge buttons only when a valid rectangular selection exists.
  • The RichTextBubbleTable component at src/components/Bubble/RichTextBubbleTable.tsx provides the standard UI for table manipulation actions.

Frequently Asked Questions

How do I disable column resizing in reactjs-tiptap-editor?

Configure the Table extension with resizable: false when initializing the editor. This removes the draggable handles from all column borders.

Can I trigger cell merging from a custom toolbar instead of the bubble menu?

Yes. Import useEditorInstance from your editor store and call editor.chain().focus().mergeCells().run() directly from any button or keyboard shortcut handler. Ensure you check editor.can().mergeCells() to validate the selection first.

What determines if the merge cells button is available?

The button availability depends on editor.can().mergeCells(), which validates that the current selection forms a proper rectangular range of adjacent table cells. If you select cells that are not contiguous or overlap existing merged cells, the command returns false and the button disables automatically.

How do I prevent users from resizing only the last column?

Set lastColumnResizable: false in the Table extension options. This keeps resizing enabled for all other columns while removing the resize handle from the right-most column border.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →