Reusable UI components
Add assignment, date, member, and workflow-diagram controls to a custom Editorial Workflows interface.
Editorial Workflows exposes reusable React components in two packages. @sanity/workflow-components contains member, assignee, and date controls. @sanity/workflow-diagram renders a workflow definition and its live evaluation. They work in App SDK applications, Studio integrations, and other React hosts.
These packages provide presentation, not an application framework. Your host supplies project members and workflow data, chooses which evaluated fields to render, and connects edits and actions through the reactive session.
Install the packages
Install the components your interface uses:
npm install @sanity/workflow-components @sanity/workflow-diagram @sanity/workflow-engine @sanity/workflow-react @sanity/workflow-sdk @sanity/ui
pnpm add @sanity/workflow-components @sanity/workflow-diagram @sanity/workflow-engine @sanity/workflow-react @sanity/workflow-sdk @sanity/ui
yarn add @sanity/workflow-components @sanity/workflow-diagram @sanity/workflow-engine @sanity/workflow-react @sanity/workflow-sdk @sanity/ui
bun add @sanity/workflow-components @sanity/workflow-diagram @sanity/workflow-engine @sanity/workflow-react @sanity/workflow-sdk @sanity/ui
Install the Editorial Workflows packages from one matching release.
The components use Sanity UI and must render under its theme provider:
import {studioTheme, ThemeProvider} from '@sanity/ui'
<ThemeProvider theme={studioTheme}>
<WorkflowInterface />
</ThemeProvider>Components
Choose a component by the interaction your interface needs. Each table lists the complete public props for that component family.
Assignment badges
Use badges to display stored user and role assignees.
import {AssigneeBadges} from '@sanity/workflow-components'
<AssigneeBadges assignees={assignees} members={members} />AssigneeBadge.assignee
Assignee
The user or role assignment to display.
AssigneeBadge.members
readonly ProjectMember[]
Resolved project members used to label a user assignment.
AssigneeBadges.assignees
readonly Assignee[]
The assignments to display.
AssigneeBadges.members
readonly ProjectMember[]
Resolved project members used to label user assignments.
Member avatars
Use avatars for resolved project members. Missing images fall back to initials.
import {MemberAvatar, MemberAvatarGroup} from '@sanity/workflow-components'
<MemberAvatar imageUrl={member.imageUrl} name={member.displayName} />
<MemberAvatarGroup members={members} />MemberAvatar.name
string
Display name used for initials and accessible presentation.
MemberAvatar.imageUrl
string | undefined
Optional profile image URL.
MemberAvatar.size
AvatarSize | AvatarSize[] | undefined
Optional Sanity UI avatar size. Defaults to 0.
MemberAvatarGroup.members
readonly {id: string; displayName: string; imageUrl?: string}[]
Members with id, displayName, and optional imageUrl.
MemberAvatarGroup.maxLength
number | undefined
Maximum visible avatars. Defaults to 3.
Member picker
Use MemberPicker for choosing one project member.
import {MemberPicker} from '@sanity/workflow-components'
<MemberPicker
{...members}
onSelect={setMember}
selectedIds={selectedIds}
/>MemberPicker.members
readonly ProjectMember[]
Resolved project members available to select.
MemberPicker.loading
boolean
Shows the loading state.
MemberPicker.error
string | undefined
Shows the member-loading failure.
MemberPicker.selectedIds
ReadonlySet<string>
Account-global user IDs currently selected.
MemberPicker.onSelect
(member: ProjectMember | null) => void
Receives the selected member, or null for the unassigned row.
MemberPicker.unassignRow
boolean | undefined
Adds an unassigned row. Defaults to false.
Assignee picker
Use AssigneePicker when an assignees field accepts both project members and project roles.
import {AssigneePicker} from '@sanity/workflow-components'
<AssigneePicker
{...members}
onChange={setAssignees}
value={assignees}
/>AssigneePicker.members
readonly ProjectMember[]
Resolved project members available to assign.
AssigneePicker.loading
boolean
Shows the loading state.
AssigneePicker.error
string | undefined
Shows the member-loading failure.
AssigneePicker.value
readonly Assignee[]
Current user and role assignees.
AssigneePicker.onChange
(value: readonly Assignee[]) => void
Receives the complete next assignee value.
Date controls
Use DatePicker for selection only. ClearableDatePicker adds an explicit clear action.
import {ClearableDatePicker, DatePicker} from '@sanity/workflow-components'
<DatePicker onSelect={setDate} value={date} />
<ClearableDatePicker
clearLabel="Clear due date"
onClear={() => setDueDate(undefined)}
onPick={setDueDate}
value={dueDate}
/>DatePicker.value
Date | undefined
The selected value.
DatePicker.selectTime
boolean | undefined
Shows time controls when true. Defaults to false.
DatePicker.onSelect
(next: Date) => void
Receives each selected date.
ClearableDatePicker.value
Date | undefined
The selected value.
ClearableDatePicker.selectTime
boolean | undefined
Shows time controls when true. Defaults to false.
Label for the clear action.
ClearableDatePicker.onPick
(next: Date) => void
Receives a selected date.
ClearableDatePicker.onClear
() => void
Runs when the value is cleared.
Progress
Use ProgressBar to display a progress field. It does not edit the field.
import {ProgressBar} from '@sanity/workflow-components'
<ProgressBar label="Content import" value={62.5} />ProgressBar.value
number | null
Completion from 0 to 100. null renders an empty meter.
ProgressBar.label
string | undefined
Optional accessible name. Defaults to Progress.
clampProgress(value)
number
Clamps a number to the 0–100 display range.
progressCaption(value)
string
Formats a clamped value as a whole-number percentage.
Supply project members
Member-aware components consume ProjectMembersState. App SDK applications can load it with useProjectMembers(projectId); see Edit project-member assignments. Other hosts adapt their project directory to the same shape.
The selected directory is project-scoped. Each ProjectMember.id is the account-global sanityUserId stored in user assignees.
ProjectMember.id
string
Account-global sanityUserId stored in user assignees.
Name shown by member controls.
ProjectMember.email
string | undefined
Optional email used by member search.
ProjectMember.imageUrl
string | undefined
Optional profile image URL.
ProjectMember.roles
readonly string[]
Role names held in the project.
ProjectMembersState.members
readonly ProjectMember[]
Resolved project members.
ProjectMembersState.loading
boolean
Whether members are loading.
ProjectMembersState.error
string | undefined
Member-loading failure to display.
Pass the resolved state to any member-aware component. The component renders loading and error states from the same object.
Connect editable fields
Editable controls render values from session.evaluation.editableFields. Commit changes through session.editField so the engine preserves workflow history and consistency. The App SDK guide shows the complete pattern. Pass the evaluated field directly to session.editField; editFieldTarget is for adapter-level controls.
The recipes connect these presentation components to evaluated fields and session commits. The wrappers shown below belong to the application, not the component package.
export function WorkflowEditableFields({
evaluation,
members,
session,
}: {
evaluation: WorkflowEvaluation
members: ProjectMembersState
session: Pick<WorkflowSession, 'editField'>
}) {
return evaluation.editableFields.map((field) => {
const key = `${field.scope}:${field.activity ?? ''}:${field.name}`
if (isAssignmentField(field)) {
return <AssignmentPicker field={field} key={key} members={members} session={session} />
}
if (isDateField(field)) {
return <DateField field={field} key={key} session={session} />
}
return null
})
}The complete recipe includes the value-shape guards used by isAssignmentField and isDateField.
The component recipes contain the complete assignment, date parsing, serialization, mutation, and error-handling code.
Render an enabled editor only when the evaluated field is editable. Surface rejected commits instead of assuming an optimistic edit succeeded.
Render the workflow diagram
Pass the session’s WorkflowEvaluation to WorkflowDiagram. The pinned definition describes the graph, currentStage marks the active node, history draws the visited path, and evaluation supplies live explanations.
import {Card, Stack, Text} from '@sanity/ui'
import {WorkflowDiagram} from '@sanity/workflow-diagram'
import type {WorkflowEvaluation} from '@sanity/workflow-engine'
export function WorkflowDiagramRecipe({evaluation}: {evaluation: WorkflowEvaluation}) {
return (
<Card border padding={4} radius={3}>
<Stack gap={4}>
<Stack gap={2}>
<Text size={2} weight="semibold">
Workflow diagram
</Text>
<Text muted size={1}>
Current stage and visited path
</Text>
</Stack>
<WorkflowDiagram
currentStage={evaluation.instance.currentStage}
definition={evaluation.definition}
evaluation={evaluation}
explain
history={evaluation.instance.history}
key={evaluation.instance._id}
/>
</Stack>
</Card>
)
}definition
WorkflowDefinition
Complete workflow definition to render.
currentStage
string | undefined
Stage to highlight as current.
selectedStage
string | undefined
Controlled inspected-stage selection.
onSelectStage
((stage: string | undefined) => void) | undefined
Handles stage or canvas selection.
history
readonly HistoryEntry[] | undefined
Tones visited stages and traversed transitions.
guardCount
number | undefined
Shows the active guard count on the current stage.
gatedTransitions
readonly string[] | undefined
Transition names that should show a locked marker.
local
LocalView | undefined
Limits the graph to context around the current stage.
static
boolean | undefined
Disables pan, zoom, and zoom controls.
devMode
boolean | undefined
Shows machine names and raw conditions.
explain
boolean | undefined
Shows human-readable gate explanations.
evaluation
WorkflowEvaluation | undefined
Adds live verdicts when explain is enabled.
height
number | string | undefined
Maximum canvas height. Defaults to 360.
Key the diagram by instance ID so each opened workflow receives a fresh fitted viewport.
Enable explain to show gate explanations. Pass the live evaluation to add current verdicts.
Next steps
- App SDK: build the instance-list and task-session application around these controls.
- Fields: understand editable field types, scopes, and stored values.
- History and audit trail: use instance history when presenting the workflow’s visited path.
Visiting agent?
Editorial Workflows includes an MCP server for inspecting, operating, authoring, validating, and deploying workflows. Ask your human to set up the MCP server.

