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 |
|---|---|---|
|
| You want the ready-made Studio interface. This page documents it. |
| The Studio adapter: | 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:
sanity6.15 or later in the 6.x linereactandreact-dom19.2.7 or laterstyled-components6.4.2 or later@sanity/sdk3.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
import {defineConfig} from 'sanity'
import {structureTool} from 'sanity/structure'
import {
workflowDefaultDocumentNode,
workflowStudioPlugin,
} from '@sanity/workflow-studio-plugin'
export default defineConfig({
// ...your projectId, dataset, schema...
plugins: [
structureTool({defaultDocumentNode: workflowDefaultDocumentNode()}),
workflowStudioPlugin({
partition: 'dev',
mappings: [
{
docType: 'article',
definition: 'article-review',
label: 'Article review',
autoStart: true,
},
],
}),
],
})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 omitspartition. Lowercase ASCII letters, digits, and dashes; an invalid value throwsContractViolationErrorwhen 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.Atagthat differs frompartitionthrowsworkflowStudioPlugin: "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.orsystem.document type. - Bind a definition that uses a
doc.reffield namedsubjectinstead of asubjectfield. - 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.andsystem.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
typesdoesn’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.
import {structureTool} from 'sanity/structure'
import {workflowsView} from '@sanity/workflow-studio-plugin'
structureTool({
defaultDocumentNode: (S, {schemaType}) =>
schemaType === 'article'
? S.document().views([S.view.form(), workflowsView(S)])
: S.document(),
})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.keyisdocument,workflow,stage,outcome,started, orended;dirisascordesc.RequiredonSort
(key: RunSortKey) => void
Reports the column a user asked to sort by. It does not reorder rows; apply the sort and pass new
rowsandsort.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}.keyisdocument,due,opened,task, orworkflow.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.
definitionsis aReadonlyMapof 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.nowis the moment overdue is measured against.forMeTaskRowsOf({rows, who})(): ForMeTaskRow[]
Selects the tasks in
rowsthatwhoholds, one row per task.whois{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.