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 contract of @sanity/workflow-studio-plugin at Workflows 0.37: the workflowStudioPlugin options, mapping rows, the structure helpers, and the table components a custom Studio tool can embed. 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 requires 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, and @sanity/workflow-components, @sanity/workflow-diagram, @sanity/workflow-engine, @sanity/workflow-react, and @sanity/workflow-studio 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, replacing whatever discovery derived for that exact docType and definition pair, or adding the pair when discovery did not produce it. One document type may appear on several rows. Two rows for the same pair throw while sanity.config.ts is evaluated, so the Studio does not start.
Without mapping rows, the plugin discovers bindings itself. It reads the latest deployed version of each definition and binds every startable workflow whose input-sourced subject field accepts a Studio schema document type. A row is needed to bind a reserved sanity. or system. type, to bind a definition that uses a doc.ref field named subject instead of a first-class subject, to enable autoStart, or to 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.
A row whose definition is not deployed, or whose seeded values do not fit the deployed definition, is disabled rather than thrown. Its start control shows Unavailable — this workflow isn’t set up correctly for <Type> documents, and the reason appears under Setup issue on that workflow's page in the Workflows tool and in the browser console, prefixed [workflow-studio-plugin]. A row rejects when it seeds an undeclared, duplicate, wrong-kind, or non-input field, a document type the subject's types does not accept, a local document type the Studio schema does not declare, or one of the engine's own document types. A required input the row leaves unset is not an error; the start dialog collects 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 renders workflow runs and ForMeTable renders tasks assigned to one person. Both keep the Workflows tool's document previews, member faces, task hints, and virtualized rows. Render them inside a Studio configured with workflowStudioPlugin, in a height-constrained container with overflowY: 'auto' so rows can scroll. For controls outside Studio, use Reusable UI components; these tables depend on Studio providers.
The tables are controlled components. Your tool loads data and renders loading, error, and empty states; filters and sorts rows before passing them in; owns selection, focus, and keyboard navigation; and resets 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.
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.
Was this page helpful?
Feedback submitted, thanks for helping us improve the docs. Have you tried these alternatives?