Workflows

Studio plugin configuration reference

Options, mapping rows, structure helpers, and embeddable tables of @sanity/workflow-studio-plugin, and how it differs from the @sanity/workflow-studio adapter.

This page lists the configuration options of @sanity/workflow-studio-plugin: the workflowStudioPlugin options, mapping rows, the structure helpers, and the table components you can embed in a custom Studio tool. To install the plugin and start a workflow from the document editor, follow Add Workflows to Sanity Studio.

Plugin and adapter packages

Package

What it provides

Use it when

@sanity/workflow-studio-plugin

workflowStudioPlugin, the document strip, the Workflows document view, the Workflows tool, structure helpers, and RunsTable/ForMeTable.

You want the ready-made Studio interface. This page documents it.

@sanity/workflow-studio

The Studio adapter: useWorkflowEngine, useWorkflowSession, useDocumentWorkflows, useWorkflowInstances, useWorkflowInstancePreviews, makeStudioObserver, and project-user helpers. The plugin is built on it.

You build your own Studio controls, such as a Document Action. Its session contract is in the reactive session and adapter reference, and its exports are in TypeDoc.

The plugin has these peer dependencies:

  • sanity 6.15 or later in the 6.x line
  • react and react-dom 19.2.7 or later
  • styled-components 6.4.2 or later
  • @sanity/sdk 3.1 or later in the 3.x line
  • @sanity/workflow-components, @sanity/workflow-diagram, @sanity/workflow-engine, @sanity/workflow-react, and @sanity/workflow-studio, all at the same version as the plugin

Example

workflowStudioPlugin

workflowStudioPlugin(config) is a Studio plugin factory. Add its return value to plugins in defineConfig. The plugin validates config when sanity.config.ts is evaluated, so a configuration error stops the Studio from starting.

WorkflowPluginConfig

WorkflowPluginConfig is the options object passed to workflowStudioPlugin. Every property is optional and read-only.

  • partition

    string, optional

    The engine partition every workflow read and write is scoped to. Must match the partition the definitions were deployed under. Defaults to default, the partition a deployment resolves to when it is alone on its workflow resource and omits partition. Lowercase ASCII letters, digits, and dashes; an invalid value throws ContractViolationError when the config is evaluated.

    When the partition holds no deployed definitions, the plugin logs a browser console warning naming the partitions on its workflow dataset that do.

  • tag

    string, deprecated

    The pre-0.37 name for partition (LegacyTagWorkflowPluginConfig). The plugin uses the value and logs [workflows] workflowStudioPlugin: Replace tag: "dev" with partition: "dev". Removing "tag" without adding "partition" selects the "default" partition instead. A tag that differs from partition throws workflowStudioPlugin: "tag" and "partition" disagree.

  • workflowDataset

    string, optional

    The dataset holding workflow definitions and instances. Defaults to the workspace's own dataset.

  • mappings

    readonly WorkflowMapping[], optional

    Rows that override or extend the bindings discovered from definition subjects. Defaults to none, in which case the effective table comes entirely from discovery.

  • effectHandlers

    Record<string, EffectHandler<SanityClient>>, optional

    Effect handlers to run in the browser while an editor has the Studio open on a workflow that commits an action. An effect with no handler registered here stays pending for another runtime to claim, which is what unattended or consequential work needs.

  • resourceClients

    (parsed: ParsedGdr) => WorkflowClient | undefined, optional

    Resolves clients for documents in other datasets, projects, or resources. Defaults to per-dataset routing derived from the Studio client. A served resource is part of the declared surface for written references, so keep the resolver identity stable: define it at module scope or memoize it.

  • previewHydration

    {pageSize?: number; interPageDelayMs?: number}, optional

    Throttles progressive loading of run previews. pageSize defaults to 500 and interPageDelayMs to 0. Intended for development harnesses simulating small pages and slow reads; production defaults need no override.

WorkflowMapping

A mapping row binds one Studio document type to one deployed definition. If the plugin already found that docType and definition pair on its own, the row replaces what it found. Otherwise, the row adds the pair. One document type can appear on several rows. Two rows for the same pair throw an error when sanity.config.ts is evaluated, and the Studio doesn’t start.

Without mapping rows, the plugin finds bindings on its own. It reads the latest deployed version of each definition. It binds every startable workflow whose subject field takes its value from user input and accepts a document type in your Studio schema.

Add a row when you need to do any of these:

  • Bind a reserved sanity. or system. document type.
  • Bind a definition that uses a doc.ref field named subject instead of a subject field.
  • Turn on autoStart.
  • Change the label, start values, or perspective.
  • docType

    string, required

    The Studio schema type this row binds. Reserved types in the sanity. and system. namespaces need an explicit row. A workflow that binds the document through a subject must explicitly accept its type. Engine-owned document types are invalid mapping targets.

  • definition

    string, required

    The name the definition was deployed under.

  • label

    string, required

    The label editors see in menus and dialogs. Required on an explicit row; discovery-derived rows fall back to the definition's title, then its name.

  • autoStart

    boolean, optional

    Starts this workflow when an editor creates a fresh document of docType in Studio. Defaults to false. Existing documents and writes outside Studio are unaffected.

  • initialStateBuilder

    (subjectGdr: GlobalDocumentReference) => InitialFieldValue[], optional

    Seeds workflow fields for a manual start. Replaces the default subject seed entirely, so an empty array seeds nothing. Does not run during auto-start.

  • contextBuilder

    (subjectGdr: GlobalDocumentReference) => StartContext, optional

    Seeds $context for the run. Not applied when the workflow starts through autoStart.

  • perspectiveField

    {name: string; required?: boolean}, optional

    Binds the run to the editor's selected Content Release through the named release.ref entry. required defaults to false, in which case the workflow starts with no perspective and uses its own defaults. When required is true, starting is blocked until an active release is selected.

If a row’s definition isn’t deployed, or its start values don’t fit the deployed definition, the plugin disables the row instead of throwing. The row’s start control then shows Unavailable — this workflow isn’t set up correctly for <Type> documents. The reason appears in two places: under Setup issue on that workflow’s page in the Workflows tool, and in the browser console, prefixed with [workflow-studio-plugin].

The plugin disables a row when it seeds any of these:

  • A field that’s undeclared, duplicated, of the wrong kind, or not filled from input
  • A document type that the subject’s types doesn’t accept
  • A local document type that the Studio schema doesn’t declare
  • One of the engine’s own document types

A required input that the row leaves unset isn’t an error. The start dialog asks the editor for it.

Structure helpers

  • workflowDefaultDocumentNode

    () => DefaultDocumentNodeResolver

    Takes no arguments. Returns a resolver giving every document type the form view plus the Workflows view; the plugin decides at runtime which workflows apply. Pass it as structureTool({defaultDocumentNode: workflowDefaultDocumentNode()}).

  • workflowsView

    (S: StructureBuilder) => ViewBuilder

    The Workflows view for a single document. Append it to a document type's own views([...]) to place the view selectively instead of on every type.

Workflow tables

RunsTable shows workflow runs, and ForMeTable shows tasks assigned to one person. Both tables look and behave like the ones in the Workflows tool, with document previews, member avatars, task hints, and virtualized rows. Render them inside a Studio that uses workflowStudioPlugin. Put them in a container with a fixed height and overflowY: 'auto', so the rows can scroll. The tables need Studio providers, so they don’t work outside Studio. For controls outside Studio, use Reusable UI components.

The tables are controlled components. Your tool does the rest:

  • Load the data, and render the loading, error, and empty states.
  • Filter and sort rows before you pass them in.
  • Manage selection, focus, and keyboard navigation.
  • Reset the scroll position when the scope or sort changes.

RunsTable

  • Requiredrows

    readonly RunRow[]

    The rows to render, already filtered and sorted. Build them with runRowsOf.

  • Requiredscope

    'open' | 'closed'

    Which columns to show: in-flight runs or settled runs.

  • RequiredscopedToWorkflow

    boolean

    Hides the workflow column when every row belongs to one workflow.

  • Requiredsort

    RunSort

    The current sort, {key, dir}, shown in the header. key is document, workflow, stage, outcome, started, or ended; dir is asc or desc.

  • RequiredonSort

    (key: RunSortKey) => void

    Reports the column a user asked to sort by. It does not reorder rows; apply the sort and pass new rows and sort.

  • RequiredonSelectRun

    (row: RunRow) => void

    Called when a user selects a row.

  • Requirednow

    Date

    The clock relative timestamps and overdue marks are measured against. Update it to refresh them.

  • RequiredonNavigateKey

    (event: RunNavigationKeyEvent) => void

    Called for keyboard input on the selected row, with {key, preventDefault}. The host owns navigation.

  • RequiredselectedId

    string | undefined

    The selected row's id. That row becomes the table's roving tab stop.

  • RequiredselectedRef

    (element: HTMLElement | null) => void

    Receives the selected row's element, for host focus and scrolling.

ForMeTable

  • Requiredrows

    readonly ForMeTaskRow[]

    The task rows to render, already filtered and sorted. Build them with forMeTaskRowsOf.

  • Requiredsort

    ForMeSort

    The current sort, {key, dir}. key is document, due, opened, task, or workflow.

  • RequiredonSort

    (key: ForMeSortKey) => void

    Reports the column a user asked to sort by. It does not reorder rows.

  • RequiredonSelect

    (row: ForMeTaskRow) => void

    Called when a user selects a row.

  • Requirednow

    Date

    The clock relative timestamps and overdue marks are measured against. Update it to refresh them.

  • RequiredonNavigateKey

    (event: RunNavigationKeyEvent) => void

    Called for keyboard input on the selected row, with {key, preventDefault}. The host owns navigation.

  • RequiredselectedId

    string | undefined

    The selected row's id. That row becomes the table's roving tab stop.

  • RequiredselectedRef

    (element: HTMLElement | null) => void

    Receives the selected row's element, for host focus and scrolling.

A row shows its task as late only when the row’s overdueSince is set. The late span counts from that date. forMeTaskRowsOf sets overdueSince while the task is open on a running workflow and one of its due dates has passed. It measures against the now you passed to runRowsOf. So derive rows again with the same clock you pass to the table as now. If you build a ForMeTaskRow yourself, set its overdueSince.

Row builders

  • runRowsOf({previews, definitions, now})(): RunRow[]

    Builds one row per instance preview. A run preview is a reduced instance record used in lists. definitions is a ReadonlyMap of deployed definitions by name, used for titles; a missing definition falls back to its raw name. Include settled parent previews so a spawned child keeps its root document attribution. now is the moment overdue is measured against.

  • forMeTaskRowsOf({rows, who})(): ForMeTaskRow[]

    Selects the tasks in rows that who holds, one row per task. who is {userId, roles}: an account-global user ID and resolved project role names. A direct assignment outranks a held role.

The package also exports the row and sort types these props use: RunRow, RunAttention, RunAttentionDetail, RunBlockedDetail, RunOverdueDetail, ForMeTaskRow, StageTask, AssignmentIdentity, TaskAssignment, TaskHolding, RunsScope, RunSort, RunSortKey, ForMeSort, ForMeSortKey, and RunNavigationKeyEvent. See TypeDoc for their fields.

Was this page helpful?