KEELSTONE_WRITE
Writes a 2D array of values to a specific location in the active workbook. By default, the written range is also converted to an Excel Table and registered as a named range — enabling the KEELSTONE_SAVE_TABLES round-trip. Pass namedRange: false to write data only, with no table or named range created.
The taskpane responds with KEELSTONE_WRITE_RESULT after the write completes.
Payload
{
type: 'KEELSTONE_WRITE';
data: (string | number | boolean | null)[][];
target?: string;
namedRange?: boolean; // default true
}
| Field | Type | Required | Description |
|---|---|---|---|
type | 'KEELSTONE_WRITE' | Yes | Discriminator |
data | 2D array | Yes | Rows and columns to write. First row is treated as headers when namedRange is true |
target | string | No | Where to write. See Target resolution |
namedRange | boolean | No | Default true. When true, wraps the written range in an Excel Table and creates a named range with an auto-generated KS_ name |
Target resolution
target value | Resolves to |
|---|---|
undefined or "" | Cell A1 of the active worksheet |
"B3" | Cell B3 on the active worksheet |
"Sheet1!B3" | Cell B3 on sheet Sheet1 |
"'My Sheet'!C5" | Cell C5 on a sheet named My Sheet |
"MyNamedRange" | The existing named range MyNamedRange |
The write range is sized to fit data automatically.
Response: KEELSTONE_WRITE_RESULT
The taskpane posts this back to the LWC after the write completes.
{
type: 'KEELSTONE_WRITE_RESULT';
tableName: string | null; // null when namedRange is false
address: string; // e.g. "Sheet1!A1:D11"
rowCount: number; // data rows written, excluding the header row
}
Listen for it in your LWC:
window.addEventListener('message', (event) => {
if (event.data?.type === 'KEELSTONE_WRITE_RESULT') {
const { tableName, address, rowCount } = event.data;
console.log(`Wrote ${rowCount} rows to ${address} as table "${tableName}"`);
}
});
Examples
Write query results to the active sheet with a table and named range (default):
window.parent.postMessage({
type: 'KEELSTONE_WRITE',
data: [
['Id', 'Name', 'Phone'],
['001...', 'Acme Corp', '555-0101'],
['001...', 'Globex', '555-0102'],
],
target: 'A1',
}, '*');
// namedRange defaults to true → Excel Table + named range created
// Response: KEELSTONE_WRITE_RESULT { tableName: 'KS_1714000000', address: 'Sheet1!A1:C3', rowCount: 2 }
Write data only — no table, no named range:
window.parent.postMessage({
type: 'KEELSTONE_WRITE',
data: [
['Acme Corp', 1250000, '2024-Q4'],
['Globex', 875000, '2024-Q4'],
],
target: 'Summary!B5',
namedRange: false,
}, '*');
// Response: KEELSTONE_WRITE_RESULT { tableName: null, address: 'Summary!B5:D6', rowCount: 2 }
Write to a named range in the workbook:
window.parent.postMessage({
type: 'KEELSTONE_WRITE',
data: [
['Metric', 'Value'],
['Total Revenue', 4500000],
['Open Opportunities', 23],
],
target: 'DashboardRange',
namedRange: false,
}, '*');
Data types
Excel accepts strings, numbers, booleans, and null. Dates should be passed as ISO strings ("2024-01-15") — Excel displays them as text unless the cell is formatted as a date.
// Good
['2024-01-15', 42500.00, true, null]
// Avoid — Date objects are not serializable over postMessage
[new Date(), ...]
Named ranges and save round-trip
When namedRange: true, the taskpane:
- Calls
context.workbook.tables.add(range, true)— first row becomes column headers - Assigns a unique name:
KS_+ timestamp - Calls
context.workbook.names.add(tableName, range)
This makes the table discoverable by KEELSTONE_SAVE_TABLES, which reads all KS_ tables on the active sheet and sends their data to /api/data/save. See KEELSTONE_SAVE_TABLES for the full round-trip.