> For AI agents: the complete Sanity documentation index is available at [https://www.sanity.io/docs/llms.txt](https://www.sanity.io/docs/llms.txt).

# 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](https://www.sanity.io/docs/workflows/studio-plugin).

## 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](https://www.sanity.io/docs/workflows/custom-studio-integrations). Its session contract is in the [reactive session and adapter reference](https://www.sanity.io/docs/workflows/custom-reactive-adapters), and its exports are in [TypeDoc](https://reference.sanity.io/_sanity/workflow-studio/). |

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

**sanity.config.ts**

```typescript
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.

#### Properties

**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, 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](https://www.sanity.io/docs/workflows/workflow-authoring-reference) field named `subject` instead of a first-class subject, to enable `autoStart`, or to change the label, start values, or perspective.

#### Properties

**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.

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

#### Properties

**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.

```typescript
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](https://www.sanity.io/docs/workflows/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`

#### Properties

**rows** (readonly RunRow[], required)

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

**scope** ('open' | 'closed', required)

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

**scopedToWorkflow** (boolean, required)

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

**sort** (RunSort, required)

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

**onSort** ((key: RunSortKey) => void, required)

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

**onSelectRun** ((row: RunRow) => void, required)

Called when a user selects a row.

**now** (Date, required)

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

**onNavigateKey** ((event: RunNavigationKeyEvent) => void, required)

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

**selectedId** (string | undefined, required)

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

**selectedRef** ((element: HTMLElement | null) => void, required)

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

### `ForMeTable`

#### Properties

**rows** (readonly ForMeTaskRow[], required)

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

**sort** (ForMeSort, required)

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

**onSort** ((key: ForMeSortKey) => void, required)

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

**onSelect** ((row: ForMeTaskRow) => void, required)

Called when a user selects a row.

**now** (Date, required)

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

**onNavigateKey** ((event: RunNavigationKeyEvent) => void, required)

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

**selectedId** (string | undefined, required)

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

**selectedRef** ((element: HTMLElement | null) => void, required)

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. 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](https://reference.sanity.io/_sanity/workflow-studio-plugin/) for their fields.

