Merging Cells
Merging combines a rectangular range of cells into a single visual cell. Only the top-left cell’s value and style are displayed; other cells in the range are hidden.
Live Demo
Merging a range
Call merge() on any RangeHandle, then set the value and style on the top-left cell.
// Merge A1:E1 into a single cell
worksheet.range('A1:E1').merge()
worksheet.cell('A1').setValue('Q3 Sales Report').setStyle({
bold: true,
fontSize: 14,
textAlign: 'center',
backgroundColor: '#0f172a',
color: '#f8fafc',
})
Row height is not adjusted automatically — set it manually when needed:
worksheet.row(0).height = 40
Unmerging
unmerge() splits the range back into individual cells.
The original top-left value is kept; the previously hidden cells become empty.
worksheet.range('A1:E1').unmerge()
Typical patterns
Title row spanning all columns
worksheet.range('A1:F1').merge()
worksheet.cell('A1')
.setValue('Annual Budget Overview')
.setStyle({ bold: true, fontSize: 13, textAlign: 'center', backgroundColor: '#0f172a', color: '#f8fafc' })
worksheet.row(0).height = 36
Grouped column headers
// "H1" spanning Q1 and Q2
worksheet.range('C1:D1').merge()
worksheet.cell('C1').setValue('H1').setStyle({ bold: true, textAlign: 'center', backgroundColor: '#1e3a5f', color: '#e2e8f0' })
// "H2" spanning Q3 and Q4
worksheet.range('E1:F1').merge()
worksheet.cell('E1').setValue('H2').setStyle({ bold: true, textAlign: 'center', backgroundColor: '#1e3a5f', color: '#e2e8f0' })
Side label spanning multiple rows
worksheet.range('A2:A5').merge()
worksheet.cell('A2').setValue('North').setStyle({
bold: true,
textAlign: 'center',
verticalAlign: 'middle',
backgroundColor: '#f1f5f9',
})
Checking merge state
Use isMerged and mergeRange on a CellHandle to inspect merge state at runtime.
const cell = worksheet.cell('A1')
if (cell.isMerged) {
const range = cell.mergeRange
// range: { topRow, leftColumn, bottomRow, rightColumn }
console.log(`Merged from row ${range.topRow} to ${range.bottomRow}`)
}
| Member | Type | Description |
|---|---|---|
isMerged | boolean | true if this cell is part of any merged range |
mergeRange | MergedCellRange | null | The full merge range, or null if not merged |
MergedCellRange uses 0-based row and column indices:
import type { MergedCellRange } from '@reogrid/lite'
const range: MergedCellRange = {
topRow: 0,
leftColumn: 0,
bottomRow: 0,
rightColumn: 4,
}
Both the anchor cell (top-left) and every covered cell return the same
mergeRange.
Behavior notes
- Only the top-left cell counts. Setting a value or style on any other cell inside a merged range has no visible effect until the range is unmerged.
- Borders work normally.
range.border()applies to the outer edges of the merged cell as expected. - xlsx round-trip. Merged cells are preserved when loading from xlsx files.
- Overlapping merges are not supported. Merging a range that overlaps an existing merge will produce undefined behavior — always unmerge first.