Helpers
Standalone utilities — pure functions that don't require the React context.
All helpers are exported from grist-widget-sdk.
Value codec
decodeGristValue(value, column?)
Decode a single cell from its wire format into a JS value.
decodeGristValue(1717599600, { type: "DateTime:UTC" })
// → Date
decodeGristValue(["L", 1, 2], { type: "RefList:Tasks" })
// → marshalled list
decodeGristValue(["E", "TypeError", "expected number"], col)
// → { kind: "error", type: "TypeError", message: "expected number" }If column is omitted, the codec applies type-inference based on the tag.
encodeGristValue(value, column)
Inverse — turn a JS value into the wire format Grist expects.
encodeGristValue(new Date(), { type: "DateTime:UTC" }) // → epoch seconds
encodeGristValue([1, 2], { type: "RefList:Tasks" }) // → ["L", 1, 2]isGristCellError(value) / coerceGristDocApiRefRowId(value)
Helpers over the wire format:
isGristCellError(value)— true whenvalueis an["E", type, message?, rowId?]tuple.coerceGristDocApiRefRowId(value)— extract the row id from aReferencecell (rawnumber,["R", "Table", id], or decoded{ rowId }).
Safe parsing
Decode + validate with per-cell issue tracking. Useful when ingesting data that may have invalid choices, broken refs, etc.
import {
safeParseGristValues,
safeParseGristTableData,
safeParseRowsToDisplayRows,
} from "grist-widget-sdk"safeParseGristValues({ values, column, helperValues?, schema?, fallbackUntypedToString?, refMode? })
Vectorised over a column: returns GristSafeParseCellResult<T>[].
type GristSafeParseCellResult<T> = {
ok: boolean
typedValue: T | null
displayValue: unknown
raw: unknown
issues: Array<{ code: string; message: string }>
}For a single cell, pass values: [cell] and read result[0].
safeParseGristTableData(data, options)
Top-level entry. Takes the columnar payload from fetchTable and returns a structured result with per-row, per-cell metadata.
const result = safeParseGristTableData<MyRow>(rawTable, {
columns: schema.columns,
displayHelpersByRef: inferDisplayHelpersByRef(rawTable),
order: "grist",
fallbackUntypedToString: true,
refMode: "detailed",
})Options (SafeParseGristTableDataOptions):
| Option | Default | Meaning |
|---|---|---|
columns | required | Map of colId → { type, ... }. |
displayHelpersByRef | {} | Map of colId → helperColId for ref/refList display columns. |
order | "grist" | "grist" (manualSort then id), "id", or "none". |
fallbackUntypedToString | true | When type === "Any", project to string for displayValue. |
refMode | "detailed" | "detailed" returns { rowId, display? } objects; "id" returns plain ids. |
safeParseRowsToDisplayRows(rows)
Projects safe-parsed rows to { id, [col]: displayValue }[]. Useful for table UIs.
inferDisplayHelpersByRef(columnar)
Scan the columnar payload for gristHelper_* columns and return the colId → helperColId map used by safeParseGristTableData.
gristCellValueSchema / gristSafeRefValueSchema / gristSafeRefListValueSchema
Zod schemas describing the decoded cell shape. Useful for custom validation pipelines.
Action builders
Builders that emit typed GristUserActionTuples. Use with w.applyActions([...]).
Records
| Builder | Tuple |
|---|---|
gristAddRecordAction(tableId, values) | ["AddRecord", tableId, null, values] |
gristUpdateRecordAction(tableId, rowId, patch) | ["UpdateRecord", ...] |
gristRemoveRecordAction(tableId, rowId) | ["RemoveRecord", ...] |
gristBulkAddRecordAction(tableId, columnarValues, rowIds?) | ["BulkAddRecord", ...] |
gristBulkUpdateRecordAction(tableId, rowIds, columnarPatch) | ["BulkUpdateRecord", ...] |
gristBulkRemoveRecordAction(tableId, rowIds) | ["BulkRemoveRecord", ...] |
Columns
| Builder | Tuple |
|---|---|
gristAddColumnAction(tableId, colId, colInfo) | ["AddColumn", ...] |
gristAddVisibleColumnAction(tableId, colId, colInfo) | ["AddVisibleColumn", ...] — also adds the column to the current view section |
gristModifyColumnAction(tableId, colId, colInfo) | ["ModifyColumn", ...] |
gristRenameColumnAction(tableId, colId, newColId) | ["RenameColumn", ...] |
gristRemoveColumnAction(tableId, colId) | ["RemoveColumn", ...] |
Tables
| Builder | Tuple |
|---|---|
gristAddTableAction(tableId, tableInfo) | ["AddTable", ...] |
gristRenameTableAction(tableId, newTableId) | ["RenameTable", ...] |
gristRemoveTableAction(tableId) | ["RemoveTable", ...] |
gristDuplicateTableAction(tableId, newTableId, options?) | ["DuplicateTable", ...] |
gristReplaceTableDataAction(tableId, rowIds, columnarData) | ["ReplaceTableData", ...] |
Document
| Builder | Tuple |
|---|---|
gristSetDocumentInfoAction(info) | ["SetDocumentInfo", info] |
Replica document
Functions for building and serialising GristReplicaDocument (see Design / Replica document).
| Symbol | Purpose |
|---|---|
buildReplicaDocumentFromDocApi(docApi, options?) | Build a typed replica from a Grist DocApi. |
buildReplicaJsonPayload(replica, options) | Compute the JSON-friendly payload (no stringification). |
serializeJsonFriendly(value) | JSON-safe deep clone (handles dates, errors). |
normalizeReplicaDocumentInput(input) | Fill defaults (e.g. omitted rows). |
normalizeReplicaTableInput(input) | Same at table level. |
decodeColumnarToReplicaRows(columnar, columns) | Convert docApi.fetchTable payload to replica rows. |
buildReplicaColumnsFromColumnar(columnar) | Infer a minimal column metadata map from raw data. |
inferGristFormulaKind(col) / resolveGristFormulaKind(col) | Detect trigger vs computed formulas. |
loadColumnMetadataByTable(docApi, options?) | Fetch _grist_Tables_column rows grouped by table. |
orderedColumnIds(columns) | Sort by parentPos, then colId. |
useGristSchema().getDocumentJson({ space }) is the React-side wrapper around buildReplicaJsonPayload + JSON.stringify — prefer it inside widgets.
Attachments
| Symbol | Purpose |
|---|---|
extractGristAttachmentIdsFromCell(cell) | All attachment ids inside an attachment cell. |
extractGristAttachmentId(cell, index?) | First (or nth) id. |
getAttachmentDownloadUrl(docApi, id, opts?) | Signed URL. |
getAttachmentDownloadUrlForCell(docApi, cell, index?, opts?) | Same, given a cell. |
fetchAttachmentBlob(docApi, id, opts?) | { blob, contentType, filename }. |
fetchAttachmentBase64(docApi, id, opts?) | { base64, contentType, filename }. |
fetchAttachmentBase64ForCell(docApi, cell, index?, opts?) | Cell variant. |
Types:
type GristFetchedAttachmentBlob = { blob: Blob; contentType: string; filename?: string }
type GristFetchedAttachment = { base64: string; contentType: string; filename?: string }REST
| Symbol | Purpose |
|---|---|
fetchWithGristAuth(docApi, path, init?) | Authenticated fetch against the doc. |
getGristAccessToken(docApi, opts?) | Cached token retrieval. |
init may include readOnly: boolean. The token cache is shared across the whole module (~45s TTL, single-flighted). Both helpers automatically retry once on a 401 after clearing the cache.
Column mapping
| Symbol | Purpose |
|---|---|
validateColumnMappings(spec, mappings) | Returns { ok, missing, emptyMultiples }. |
The spec is the same value passed as options.columns to useGrist() / <GristWidgetProvider>; mappings is the latest map from useGrist().mappings. Use this for tests and to power gated UI outside of React.
For the React-side equivalents (resolveMappedColumnId(name) and mapBack(patch)), see useGrist — they live on the hook because they close over the current mapping snapshot.
Widget options
| Symbol | Purpose |
|---|---|
parseGristWidgetOptionsCell(cell) | Best-effort JSON parse for a WidgetOptions cell. |
normalizeGristChoiceListEntries(input) | Coerce ["L", "a", "b"], string[], string, or null into string[]. |
Column filters
| Symbol | Purpose |
|---|---|
isGristNoiseColumn(colId, col?, options?) | True for id, gristHelper_*, manualSort, logging columns. |
Alerts
import { getGristSdkAlertDescriptors, formatColumnMappingAlertMessage } from "grist-widget-sdk"
const alerts = getGristSdkAlertDescriptors(useGrist(), {
columnMappingHint: "Open the gear icon to map columns.",
mapBackHint: "Pick a single column to write to.",
})Returns:
type GristSdkAlertDescriptor = {
id: string
kind: GristSdkAlertKind
title: string
severity: "info" | "warning" | "error"
ariaRole: "status" | "alert"
message: string
}Helpers: getGristSdkAlertTitle, getGristSdkAlertSeverity.
You render these with your own Alert component. The SDK doesn't ship UI styles — map severity to your tokens.