Editorial Workflows

Custom reactive adapters

Connect Editorial Workflows to an unsupported host or data layer by implementing the store-agnostic reactive observer contract.

Prerelease

Use the Studio plugin inside Sanity Studio and the App SDK adapter in a custom application. Build another adapter only when a different host or data layer must drive the reactive session.

Build your own adapter

@sanity/workflow-react is store-agnostic. An adapter connects its hook core to a reactive document store by implementing WorkflowObserver, four useSyncExternalStore-shaped reads plus one write:

type ObserverState<T> =
  | {status: 'loading'}
  | {status: 'ready'; value: T}
  | {status: 'invalid'; invalid: InvalidDoc}
  | {status: 'error'; error: unknown}

interface DocStore<T> {
  subscribe: (onChange: () => void) => () => void
  getSnapshot: () => ObserverState<T>
}

interface ObservedInstances {
  instances: readonly WorkflowInstance[]
  unreadable: readonly InvalidDoc[]
}

interface WorkflowObserver {
  observeInstance: (instanceId: string) => DocStore<WorkflowInstance | null>
  observeDocs: (
    documents: SubscriptionDocument[],
    perspective: WorkflowPerspective | undefined,
  ) => DocStore<ReadonlyMap<string, SanityDocument | null>>
  observeGuards: (args: {
    instanceId: string
    resources: readonly DatasetResourceId[]
    sharedIds?: readonly string[]
  }) => DocStore<readonly MutationGuardDoc[]>
  observeInstances: (query: CompiledQuery) => DocStore<ObservedInstances>
  ensureDocumentExists: (args: EnsureDocumentExistsArgs) => Promise<void>
}

Every store starts in loading and resolves to ready, invalid, or error; a later successful emission clears either failure. Content documents pass through unvalidated. The instance and guard wrappers surface validation failure through the store’s wholesale invalid state. validatedInstancesStore instead validates each list row and returns {instances, unreadable} in a ready snapshot. Keep resolved snapshots referentially stable, including the shared NO_GUARDS identity for an empty guard list. The builder kit lives at @sanity/workflow-react/observer: validatedInstanceStore, validatedGuardsStore, validatedInstancesStore, combineDocStores, combineGuardStores, ensureDocumentExists, and useKeyed. Pass the completed observer to the hooks from @sanity/workflow-react.

Next steps

Visiting agent?

Was this page helpful?