Grist Widgets

SDK

Add grist-widget-sdk to an existing Vite or Next.js project, then the core hooks every widget uses.

grist-widget-sdk wraps the Grist plugin API — the JavaScript bridge between a custom widget iframe and a Grist document — into one hook, a provider, and a boundary. New here? What is grist-widget-sdk? covers why it exists and when to reach for the raw plugin API instead.

Already have a scaffolded project from the template or the CLI? Its dependencies and plugin-api script tag are pre-wired — skip ahead to Hello world. This page is for adding the SDK to a project you already have.

Install

pnpm add grist-widget-sdk
# or
npm install grist-widget-sdk
# or
yarn add grist-widget-sdk

Peer dependencies:

react       >=18
react-dom   >=18

Load the Grist plugin API

The SDK expects the global grist object at runtime. Add the script in your app shell:

<script src="https://docs.getgrist.com/grist-plugin-api.js"></script>

Hello world

hello-world is the canonical minimal widget. It is type-checked on every pnpm typecheck:examples run (root pnpm test includes that step).

import { useGrist, type UseGristOptions } from "grist-widget-sdk"

export const GRIST_OPTIONS: UseGristOptions = {
  requiredAccess: "read table",
}

export function WidgetApp() {
  const w = useGrist()
  const rowKey =
    w.record && typeof w.record.id === "number" ? String(w.record.id) : w.mode

  if (w.mode === "empty") return <p>Select a row.</p>
  if (w.mode === "new-row") return <p>New row flow</p>
  return <p key={rowKey}>Selected row #{String(w.record!.id)}</p>
}

Wrap it with GristWidgetProvider + GristBoundary — the template does exactly this in main.tsx. That's everything you need to:

  • Wait for Grist to finish its handshake.
  • Render a friendly fallback when the page is opened outside Grist.
  • Render an error UI if anything goes wrong, with a retry button.
  • Subscribe to the currently selected row.
  • Switch between empty / row / new-row modes.

See Provider & boundary for what each piece does, and Raw plugin API vs SDK for why you use this package instead of calling grist directly.

What useGrist() returns

GroupProperties
Statusstatus, isAvailable, isReady, error, reload()
Selectionrecord, records, mappedRecord, mode, mappings, columnMappingStatus, isNewRecord
Writes (records)table, getTable(id), actionStatus, actionError
Writes (schema)applyActions(actions)
ReadsfetchTable, fetchTableRows, fetchRow, fetchSelectedTable, fetchSelectedRecord, listTables, getDocName
Widget optionswidgetOptions, getWidgetOptions, setWidgetOption, patchWidgetOptions, clearWidgetOptions
LinkingsetCursorPosition, setLinkedRowSelection
Attachments / RESTgetAttachmentUrl, fetchAttachmentBlob, uploadAttachment, getAccessToken, fetchWithAuth
Section APIconfigure, refreshMappings, currentTableId
Themetheme ("light" | "dark" | null)

See the API reference for every field.

Asking for write access and declared columns

declared-columns shows GRIST_OPTIONS.columns and columnMappingStatus — full source in the Cheat sheet under Mappings + column gate.

In the widget configuration panel, the user maps these logical names to real columns. You consume them via w.mappedRecord and w.mapBack(...) on writes — see Column mapping.

Run a write

mark-done is a single-row table.update; its source is in the Cheat sheet.

For bulk writes, pass an array and the SDK forwards it to Grist as one BulkUpdateRecord action — see bulk-mark-done in the Cookbook.

For schema changes, see schema-migration, also in the Cookbook.

Explore the rest of the SDK

Guide contents

TopicWhat it covers
Raw plugin API vs SDKWhy use this package instead of calling grist directly
Column mappingDeclaring logical column names, letting users map them
Reading dataThe two paths for reading data out of a document
Writing dataThe two write paths, and when to use each
Widget optionsPersisting per-section settings in Grist's own store
Attachments & RESTAttachment bytes, SQL, and the REST API via pre-authenticated fetch
Widget linking & themeDriving another widget's cursor; adopting the document theme
Typing your rowsThe two generics on useGrist()
Handshake state machineHow a widget and Grist stay in sync
Error handlingWhere errors surface, and which layer to read
PerformanceKeeping large widgets fast with slice hooks
Cheat sheetCopy-pasteable shapes for daily reference
TroubleshootingSymptom-first fixes
CookbookTen ready-to-paste recipes

See the API reference for every exported symbol, or Design for the rationale behind the API's shape.

Where to go next

  • What is grist-widget-sdk? — why it exists, and when to reach for the raw plugin API instead.
  • Core concepts — the mental model behind selection modes, mappings, and the ready handshake.
  • CLI reference — scaffold a new project instead of adding the SDK to an existing one.
  • Emulator — test widgets without a live Grist document.

On this page