Open questions
Things we should decide before locking the 1.0 surface. Each entry is small enough to debate in a single discussion thread.
Things we should decide before locking the 1.0 surface. Each entry is small enough to debate in a single discussion thread.
Resolved questions move into the relevant
/guide/or/api/page in the same iteration; see the workflow for the process.
Marker legend: ๐ = API surface โข ๐ = Behavior โข ๐ = Types โข ๐ = Docs only
Naming
๐
useGrist vs useGristWidget
useGrist() is short, but the package is grist-widget-sdk. Are we losing clarity by dropping "widget"? Alternative: useGristWidget() as canonical, useGrist as alias.
Vote: keep useGrist for brevity, document the rationale.
๐
mappedRecord vs record.mapped
Today mappedRecord lives flat on the return object. Some users expect record.mapped. Trade-off: discoverability vs flatness.
Vote: keep flat โ drilling down to record.raw / record.mapped reads worse.
๐
Slice hook plurals (useGristSelection / useGristWrites)
useGristWrites is plural ("writes"), useGristSelection is singular. Consistent would be useGristWrite() or useGristSelections(). Both feel awkward.
Vote: accept the inconsistency; document.
Behavior
๐
mapBack and allowMultiple columns
Reverse-mapping is ambiguous when multiple real columns share one logical name. Current behavior: skip with a console warning.
Options:
- Skip silently โ minimal surprise, possibly silent data loss.
- Throw โ forces the user to pick. Breaking change.
- Skip + surface in
actionError/ alerts โ recommended.
Vote: option 3.
๐
widgetOptions debounce
patchWidgetOptions(...) is sync today, no debounce. The advanced useGristWidgetOptions debounces. Should the primary surface also debounce?
Options:
- No debounce in primary โ predictable, consistent with other writes.
- Configurable debounce on provider โ adds knobs.
Vote: option 1. If you need debounce, use useGristWidgetOptions.
๐
actionStatus reset
When does actionStatus go back to "idle" after "error"? Options:
- Sticky until next action โ current.
- Auto-reset on next emit โ bad, user can't render the error.
clearActionError()method onuseGristโ adds surface.
Vote: option 1, document the staleness.
๐
unavailableGraceMs
Default 5000 ms. Some environments (slow corporate VPN, embedded iframes) reportedly need >10s. Should we expose this as a global env / provider option?
Vote: it's already on <GristBoundary> โ fine. Document the failure mode.
๐
fetchTableRows({ safeParse: true }) default
When columns is given but safeParse is not, do we still decode types? Today: yes (we always decode if columns is given). Should that be opt-in?
Vote: keep current; an opt-out feels strictly worse.
๐ Token cache scope
Today the REST token cache is module-level, shared across all useGrist() instances. Should it be per-provider?
Vote: keep module-level. Two providers in one app is undefined behavior anyway.
Types
๐
GristRowRecord id field
Today id: number. Grist also accepts string row ids in some contexts (formulas, references). Should we allow number | string?
Vote: keep number. The wire actually uses numbers; strings are rare formula-side artefacts that wouldn't appear in widget reads.
๐ Mapping shape
mappings: Record<string, string | string[]>. The string[] only appears when allowMultiple is true. Should we encode this in the type with a tagged union?
type GristMapping =
| { kind: "single"; colId: string }
| { kind: "multi"; colIds: string[] }Trade-off: clearer at consumption, more friction in the common case (mappings.Title becomes mappings.Title.colId).
Vote: keep flat. Document the string | string[] rule.
๐ Generic defaults
useGrist() defaults TRow and TMapped to GristRecordFields (i.e. Record<string, unknown>). Should we default to never to force users to opt in?
Vote: keep GristRecordFields. Forcing generics on every call hurts DX.
Replica document
๐
Include selection by default
Today useGristSchema() includes selection when there's a current row. Should it always include the selection block (even when empty), so consumers can rely on its presence?
Vote: always include it (with mode: "empty").
๐
References in GristReplicaColumn
For ref/refList columns, today the type is "Ref:Tasks". Should we expose the parsed { type: "Ref", target: "Tasks" }?
Vote: add references?: string alongside type: string โ backward-compatible, lets consumers skip parsing.
๐
widgetOptions per column
GristReplicaColumn.widgetOptions is Record<string, unknown>. Should we type the common cases (Choice list values, numeric format, alignment)?
Vote: add a GristReplicaColumnWidgetOptions type with optional well-known fields, keep the index signature for unknown ones.
๐
.replica.json vs .grist.json
We have both file extensions floating around. Pick one.
Vote: .replica.json. Document the migration in the changelog.
Tests
๐
renderWithGrist re-exports
Today users still need import { screen } from "@testing-library/react". Should renderWithGrist's entry re-export the testing-library surface?
Vote: yes, re-export screen, fireEvent, waitFor. Saves one import line.
๐ Built-in matchers
Should we ship expect.toHaveAppliedAction(...) / expect.toHaveEmittedEvent(...) matchers?
Vote: no โ provide actionsOf / eventsOf + waitForEvent and let the user write whatever assertion shape they want.
Documentation
๐ Quickstart for non-Vite stacks
Today the Getting Started page shows Vite + Next.js. Should we add Astro, Remix, plain HTML?
Vote: Add plain HTML (the most "outside" framework case). Skip Astro/Remix; same pattern as Next.js, link there.
๐ Migration page
We'll iterate quickly during 0.x. Should we maintain a MIGRATION.md in the docs site?
Vote: yes, under /design/migration.md (to be added when we have a meaningful diff).
Pending API tightenings
Forward-looking adjustments to the public surface that are decided
in principle but not yet scheduled into an iteration. When one is
picked up, it gets a task-NNN on the task board, is agreed in
chat, and is removed from this list when shipped.
(Empty after 0.2 closeout โ slice hooks, mapBack alerts, safeParse
union, presets, and RTL re-exports shipped. Track 0.3+ work on the
task board.)
Out of scope
Decisions explicitly not on the table for 1.0:
- Non-React framework support โ see principle #10.
- A "form" hook that binds inputs to
record/mapBackโ out of scope (user space). - Built-in toast / alert UI โ we ship descriptors; user owns rendering.
- Multi-document support โ Grist plugin API doesn't expose this.