Workspace Context
Learn how to create workspace contexts that manage shared state and enable communication between extensions in a workspace.
Workspace Contexts serve as the central communication hub for workspace extensions, providing shared state management within workspace boundaries. They enable different workspace components to interact through a common data layer.
Purpose
Workspace Contexts provide:
Shared state scoped to a specific workspace instance
Communication layer between extensions in the workspace
Entity lifecycle management for workspace data
Context isolation ensures workspace independence
Manifest
{
type: 'workspaceContext',
name: 'Example Counter Workspace Context',
alias: 'example.workspaceContext.counter',
api: () => import('./counter-workspace-context.js'),
conditions: [
{
alias: UMB_WORKSPACE_CONDITION_ALIAS,
match: 'Umb.Workspace.Document',
},
],
}Implementation
Create a workspace context by extending UmbContextBase and providing a unique context token. Add this to your project to enable shared state management between workspace extensions:
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { UmbNumberState } from '@umbraco-cms/backoffice/observable-api';
export class WorkspaceContextCounterElement extends UmbContextBase {
#counter = new UmbNumberState(0);
readonly counter = this.#counter.asObservable();
constructor(host: UmbControllerHost) {
super(host, EXAMPLE_COUNTER_CONTEXT);
}
increment() {
this.#counter.setValue(this.#counter.value + 1);
}
reset() {
this.#counter.setValue(0);
}
}
export const api = WorkspaceContextCounterElement;
export const EXAMPLE_COUNTER_CONTEXT = new UmbContextToken<WorkspaceContextCounterElement>(
'UmbWorkspaceContext',
'example.workspaceContext.counter',
);Context Token Pattern
Always use 'UmbWorkspaceContext' as the first parameter in your context token to ensure proper workspace scoping and isolation:
export const MY_WORKSPACE_CONTEXT = new UmbContextToken<MyWorkspaceContext>(
'UmbWorkspaceContext', // Ensures workspace scoping
'my.extension.alias', // Must match manifest alias
);Workspace Lifecycle
Initialization
Created when workspace loads
Available to all extensions within that workspace
Destroyed when workspace closes
Scoping
Context instances are isolated per workspace
Extensions can only access contexts from their own workspace
Context requests automatically scope to the nearest workspace
Conditions
Workspace contexts only initialize when their conditions match:
conditions: [
{
alias: UMB_WORKSPACE_CONDITION_ALIAS,
match: 'Umb.Workspace.Document', // Only available in document workspaces
},
],Entity Data Patterns
Draft State Management
export class EntityWorkspaceContext extends UmbContextBase {
#entity = new UmbObjectState<MyEntity | null>(null);
#isDirty = new UmbBooleanState(false);
readonly entity = this.#entity.asObservable();
readonly isDirty = this.#isDirty.asObservable();
updateEntity(changes: Partial<MyEntity>) {
const current = this.#entity.getValue();
if (current) {
this.#entity.setValue({ ...current, ...changes });
this.#isDirty.setValue(true);
}
}
}Server Integration
export class ServerEntityContext extends UmbContextBase {
#repository = inject(MyEntityRepository);
async save() {
const entity = this.#entity.getValue();
const saved = await this.#repository.save(entity);
this.#entity.setValue(saved);
this.#isDirty.setValue(false);
}
}Extension Communication
In Workspace Actions
export class MyWorkspaceAction extends UmbWorkspaceActionBase {
override async execute() {
const context = await this.getContext(MY_WORKSPACE_CONTEXT);
context.performAction();
}
}In Workspace Views
export class MyWorkspaceView extends UmbElementMixin(LitElement) {
constructor() {
super();
this.consumeContext(MY_WORKSPACE_CONTEXT, (context) => {
this.observe(context.data, (data) => this.requestUpdate());
});
}
}Best Practices
State Encapsulation
// ✅ Private state with public observables
#data = new UmbObjectState(initialData);
readonly data = this.#data.asObservable();
// ❌ Direct state exposure
data = new UmbObjectState(initialData);Context Token Consistency
// ✅ Use workspace scoping
new UmbContextToken<T>('UmbWorkspaceContext', 'my.alias');
// ❌ Generic context (not workspace-scoped)
new UmbContextToken<T>('MyContext', 'my.alias');Conditional Availability
Only provide contexts when they are meaningful for the workspace type.
Last updated
Was this helpful?