Two Ranges
ICellRange and IRange
SheetXL provides two range APIs designed to work together: one for interacting with the sheet, and one for manipulating values. Most applications use both — ICellRange to read from and write to the sheet, and IRange for the computation in between.
Comparison
Most developers will only work with one API — ICellRange for ui development or sheet automation, IRange for data-intensive logic or UDF authoring.
| ICellRange | IRange | |
|---|---|---|
| Purpose | Sheet interaction | Value computation |
| Stateful? | Yes — knows about cells, sheets, protection, transactions | No — portable across threads, workers, and server environments |
| Design | Imperative, like Office Scripts | Functional/immutable, like NumPy |
| Primary use | AI agents, macros, sheet automation | Data manipulation, formula authoring |
| Updates | Immediate (or batched); supports undo | Returns new ranges; no side effects |
ICellRange - Sheet Interaction - Live Layer
ICellRange is your handle to the live sheet. Think of it like Excel's Range object — it knows where it is, what sheet it belongs to, and how to commit changes. It respects protection, participates in transactions, and integrates with undo history.
Studio (Our Interactive UI) only uses
ICellRange
Modelled after 'Office Scripts' to feel natural for Excel developers.
IRange - Value Computation - Logic Layer
IRange is a stateless, immutable value object. It has no knowledge of the sheet — only data shapes, broadcasting rules, coercion, and formula semantics. Because it's stateless, the same IRange logic runs identically in a browser, a Node.js server, or a Web Worker.
Formulas and UDFs only use
IRange
Modelled after 'NumPy' to feel natural to data scientists and pandas developers.
How They Work Together
ICellRange and IRange connect in a variety of ways:
getValuesRange()exposes the cell range's values as anIRange.setComputedValues()is a convenience wrapper that hands you that sameIRange, applies your transform, and writes the result back — all in one call.- Any
ICellRangemethod that accepts a range accepts either anICellRangeorIRangedirectly. - UDFs (user-defined functions) work natively with
IRange.
// setValues accepts an IRange directly
const computed = range.replace([[1, 2, 3]]).repeat(3, 1)
cellRange.setValues(computed)
// tiles the currently selected range
// you return a transformed IRange, and it's written back to the sheet.
getSelectedRange().setComputedValues(r => r.repeat(3, 2), { autoSelect: true, description: 'Tile Values'});
// Equivalent explicit form:
const result = cellRange.getValuesRange().replace([[1, 2, 3]]).repeat(3, 1)
cellRange.setRange(result)
Examples
The following examples show equivalent operations using each API. See the API Reference and Examples Gallery for the full list.
Setting Values Directly
// ICellRange — write a row of values to the sheet immediately
cellRange.setValues([[1, 2, 3]])
// IRange — produce a new range with those values (no sheet side effect)
const result = range.replace([[1, 2, 3]])
Broadcasting and Masking (IRange)
IRange supports NumPy-style broadcasting. repeat() repeats the values to fill the target shape; mask() applies a boolean condition to filter or gate values — similar to boolean indexing in pandas or NumPy.
// Repeat [1,2,3] across 3 rows, then apply a boolean mask
const result = range
.replace([[1, 2, 3]])
.repeat(3, 1) // repeat(rows, cols): repeat to 3×3
.mask(condition) // condition: IRange<boolean>
Sparse Updates
Use sparse updates when you need to set a small number of cells within a larger range without rewriting the whole thing.
// ICellRange — addresses are absolute cell references (e.g. 'A1')
cellRange.createSparseBuilder()
.push('A1', 2)
.push('A2', 567)
.apply() // note - apply for immediate update
// IRange — coordinates are relative to the range origin (row, col)
const updated = range.sparseBuilder()
.push(0, 0, 2) // row 0, col 0
.push(1, 0, 567) // row 1, col 0
.build() // note - build for return new range
ICellRange: uses absolute sheet addresses (
'A1'). IRange: uses zero-based coordinates relative to the range's top-left corner.
Reusable Callbacks
Because IRange transforms are plain functions, they're easy to reuse — across sheets, in UDFs, or in tests. Define the transform once and apply it wherever you need it.
// A reusable IRange transform
const applyDefaults = (r: IRange): IRange =>
r.replace([[1, 2, 3]]).repeat(3, 1)
// Apply via ICellRange — transforms the cell range's IRange and writes back to sheet
cellRange.setComputedValues(applyDefaults)
// Apply to a standalone IRange — no sheet interaction
const result = range.withComputedValues(applyDefaults)
Working example
// 1. Get our handle to the LIVE sheet (ICellRange)
const priceCells = workbook.getSheet("Inventory").getRange("B2:B100");
// alternative get
// const priceCells = workbook.getSheet("Inventory").getRange("Inventory!B2:B100");
// 2. Use the "Hand-off" method
priceCells.setComputedValues((data: IRange) => {
// We are now in the Logic Layer.
// 'data' is a stateless IRange.
const taxRate = 1.10; // could use an async service if function is async
// Perform NumPy-style math
return data
.toType('number') // Ensure we're working with numbers, this will auto-coerce as appropriate
.map(v => v * taxRate) // Apply tax
.fillEmpty(0); // Clean up data gaps
});