Skip to content

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.

ts
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.

ts
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 when value is an ["E", type, message?, rowId?] tuple.
  • coerceGristDocApiRefRowId(value) — extract the row id from a Reference cell (raw number, ["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.

ts
import {
  safeParseGristValues,
  safeParseGristTableData,
  safeParseRowsToDisplayRows,
} from "grist-widget-sdk"

safeParseGristValues({ values, column, helperValues?, schema?, fallbackUntypedToString?, refMode? })

Vectorised over a column: returns GristSafeParseCellResult<T>[].

ts
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.

ts
const result = safeParseGristTableData<MyRow>(rawTable, {
  columns: schema.columns,
  displayHelpersByRef: inferDisplayHelpersByRef(rawTable),
  order: "grist",
  fallbackUntypedToString: true,
  refMode: "detailed",
})

Options (SafeParseGristTableDataOptions):

OptionDefaultMeaning
columnsrequiredMap of colId → { type, ... }.
displayHelpersByRef{}Map of colId → helperColId for ref/refList display columns.
order"grist""grist" (manualSort then id), "id", or "none".
fallbackUntypedToStringtrueWhen 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

BuilderTuple
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

BuilderTuple
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

BuilderTuple
gristAddTableAction(tableId, tableInfo)["AddTable", ...]
gristRenameTableAction(tableId, newTableId)["RenameTable", ...]
gristRemoveTableAction(tableId)["RemoveTable", ...]
gristDuplicateTableAction(tableId, newTableId, options?)["DuplicateTable", ...]
gristReplaceTableDataAction(tableId, rowIds, columnarData)["ReplaceTableData", ...]

Document

BuilderTuple
gristSetDocumentInfoAction(info)["SetDocumentInfo", info]

Replica document

Functions for building and serialising GristReplicaDocument (see Design / Replica document).

SymbolPurpose
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

SymbolPurpose
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:

ts
type GristFetchedAttachmentBlob = { blob: Blob; contentType: string; filename?: string }
type GristFetchedAttachment = { base64: string; contentType: string; filename?: string }

REST

SymbolPurpose
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

SymbolPurpose
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

SymbolPurpose
parseGristWidgetOptionsCell(cell)Best-effort JSON parse for a WidgetOptions cell.
normalizeGristChoiceListEntries(input)Coerce ["L", "a", "b"], string[], string, or null into string[].

Column filters

SymbolPurpose
isGristNoiseColumn(colId, col?, options?)True for id, gristHelper_*, manualSort, logging columns.

Alerts

ts
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:

ts
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.

Released under the ISC License.