Skip to content

Cell Merging

MergeManager handles cell merging with an O(1) spatial index for fast lookups. Each merged region has an anchor cell (top-left) that holds the content and rendered text. All other cells in the region become hidden — they exist in the data model but are not rendered individually. The anchor cell expands visually to cover the full merged area.

Live Demo
Three merged regions shown: vertical (rows 1-3 in Department), horizontal (row 5 across Name-Salary), and vertical (rows 8-10 in ID).
View source code
MergingDemo.tsx
import { useRef, useEffect } from 'react';
import { WitTable } from '@witqq/spreadsheet-react';
import type { WitTableRef } from '@witqq/spreadsheet-react';
import { DemoWrapper } from './DemoWrapper';
import { generateEmployees, employeeColumns } from './generate-data';
import { useSiteTheme } from './useSiteTheme';
const data = generateEmployees(50);
export function MergingDemo() {
const { witTheme } = useSiteTheme();
const tableRef = useRef<WitTableRef>(null);
useEffect(() => {
const engine = tableRef.current?.getInstance();
if (!engine) return;
const mm = engine.getMergeManager();
mm.merge({ startRow: 0, startCol: 2, endRow: 2, endCol: 2 });
mm.merge({ startRow: 4, startCol: 1, endRow: 4, endCol: 3 });
mm.merge({ startRow: 7, startCol: 0, endRow: 9, endCol: 0 });
engine.requestRender();
}, []);
return (
<DemoWrapper title="Live Demo" description="Three merged regions shown: vertical (rows 1-3 in Department), horizontal (row 5 across Name-Salary), and vertical (rows 8-10 in ID)." height={440}>
<WitTable
theme={witTheme}
ref={tableRef}
columns={employeeColumns}
data={data}
showRowNumbers
style={{ width: '100%', height: '100%' }}
/>
</DemoWrapper>
);
}
import { useRef } from 'react';
import { WitTable, WitTableRef } from '@witqq/spreadsheet-react';
function App() {
const ref = useRef<WitTableRef>(null);
const handleMerge = () => {
// Merge cells from A1 to C3 (rows 0-2, cols 0-2)
ref.current?.getInstance().getMergeManager().merge({
startRow: 0, startCol: 0,
endRow: 2, endCol: 2,
});
};
const handleUnmerge = () => {
ref.current?.getInstance().getMergeManager().unmerge(0, 0);
};
return (
<>
<button onClick={handleMerge}>Merge A1:C3</button>
<button onClick={handleUnmerge}>Unmerge</button>
<WitTable ref={ref} columns={columns} data={data} />
</>
);
}
const mm = ref.current?.getInstance().getMergeManager();
// Check if a cell is the anchor (top-left) of a merged region
mm.isAnchorCell(0, 0); // true
// Check if a cell is hidden by a merge
mm.isHiddenCell(1, 1); // true
// Get the merged region containing a cell
const region = mm.getMergedRegion(1, 1);
// { startRow: 0, startCol: 0, endRow: 2, endCol: 2 }
// List all merged regions
const all = mm.getAllRegions();
MethodSignatureDescription
merge(region: MergedRegion) => booleanMerge cells in region
unmerge(startRow: number, startCol: number) => booleanUnmerge cells starting at position
getMergedRegion(row: number, col: number) => MergedRegion | nullGet region containing cell
isAnchorCell(row: number, col: number) => booleanIs cell the merge anchor
isHiddenCell(row: number, col: number) => booleanIs cell hidden by merge
getAllRegions() => MergedRegion[]List all merged regions
interface MergedRegion {
startRow: number;
startCol: number;
endRow: number;
endCol: number;
}