Skip to content
Draft
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,24 +1,158 @@
---
description: Establish the bond for extensions to communication across the application
description: Global contexts in Umbraco provide a clean, type-safe way to share functionality across the backoffice.
---

# Global Context

{% hint style="warning" %}
This page is a work in progress and may undergo further revisions, updates, or amendments. The information contained herein is subject to change without notice.
{% endhint %}
The Global Context extension type allows extension authors to create a custom context of data and functions and make it available across the entire backoffice. Unlike other contexts that might be scoped to a specific workspace or view, a global context is available everywhere. These contexts are kept alive through the entire backoffice session.

A global context manages the logic code from your Lit Element controllers.
Consider using a global context when you need to:

- **Share state across multiple extensions** - Track user preferences or application-wide settings
- **Provide utility functions** - Common operations that multiple parts of an extension
- **Manage centralized services** - API clients, notification handlers, or data repositories
- **Coordinate between different extensions** - Communication layer between dashboards, property editors, and other components
- **Share functionality with other extensions** - Allow other package developers to access features and functionality within the package

Extension authors should prefer to use other context types [Workspace Contexts](workspaces/workspace-context.md) over Global Contexts. Umbraco itself uses Global Contexts sparingly, for clipboard, current user, and icons.

## Registration of a Global Context

You can register a global context like so:
Global Context extensions can be registered using a manifest file like `umbraco-package.json`.

```typescript
{% code title="umbraco-package.json" %}
```json
{
type: 'globalContext',
alias: 'My.GlobalContext.Products',
name: 'My Products Context',
api: 'my-products.context.js',
"$schema": "../../umbraco-package-schema.json",
"name": "My Global Context Package",
"version": "1.0.0",
"extensions": [
{
"type": "globalContext",
"alias": "My.GlobalContext",
"name": "My Global Context",
"api": "/App_Plugins/my-global-context/dist/my-context.context.js"
}
]
}
```
{% endcode %}

**Key fields:**
- `type`: Must be `"globalContext"`, see: [ManifestGlobalContext](https://apidocs.umbraco.com/v16/ui-api/interfaces/packages_core_extension-registry.ManifestGlobalContext.html)
- `alias`: A unique identifier for the context
- `name`: A human-readable name
- `api`: The path to the compiled JavaScript file

## Creating Your Global Context

### 1. Define a Context Token

First, create a context token. This is how other parts of an extension will reference the context:

{% code title="src/my-context.ts" %}
```typescript
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';

export interface MyGlobalContextInterface {
getCurrentUser(): Promise<string>;
setPreference(key: string, value: any): void;
getPreference(key: string): any;
getHostElement(): Element;
}

export const MY_GLOBAL_CONTEXT = new UmbContextToken<MyGlobalContextInterface>(
'My.GlobalContext'
);
```
{% endcode %}

### 2. Implement the Context Class

Next, implement the context class:

{% code title="src/my-context.ts" %}
```typescript
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
import { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';

export class MyGlobalContext extends UmbContextBase<MyGlobalContextInterface> implements MyGlobalContextInterface {
#preferences: Map<string, any> = new Map();

constructor(host: UmbControllerHost) {
super(host, MY_GLOBAL_CONTEXT);
}

async getCurrentUser(): Promise<string> {
// In a real implementation, you'd fetch this from the API
return 'Current User';
}

setPreference(key: string, value: any): void {
this.#preferences.set(key, value);
console.log(`Preference set: ${key} = ${value}`);
}

getPreference(key: string): any {
return this.#preferences.get(key);
}

getHostElement(): Element {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getHostElement() is required by UmbContextMinimal, but I'm not sure what the intent of this is? To return a DOM element? The DOM element that the context is attached to? That's kind of how it seems to be getting used in the Umbraco backoffice samples I've found, but it seems odd.

https://apidocs.umbraco.com/v16/ui-api/interfaces/libs_context-api.UmbContextMinimal.html#gethostelement-1

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, it seems odd. We need @nielslyngsoe's perspective on this. We could provide any value as a context, including simple types such as string, number, and array, I thought.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my Umbraco project I'm using for testing all of this docs code, I just did return new Element(); inside of there to keep the project compiling, but I feel dirty publishing that without further comment or without understanding what might blow up.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi everyone :-)

The UmbContextMinimal abstraction/interface might confuse matters, so for understanding, I would say ignore that one. It only exists to avoid circular dependencies and separation of concerns.

So the particular need for the getHostElement is that this is the only part of a UmbControllerHost that the Context implementation must have.
But it could have been simpler if the example interface extended UmbControllerHost. But for someone not basing their class on UmbContextBase(Which is an extension of UmbControllerBase) will then have a more complex interface to adhere to β€” so that could be the downside to it, but maybe only relevant for complex use cases, like someone implementing a different framework, so maybe an aspect to ignore.

So you could avoid declaring the getHostElement by making the interface extend UmbControllerHost β€” this is all handled, from an implementation perspective, as your class extends UmbContextBase.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... and I may add that the getHostElement is supposed to return the element that hosts the controllers. For a Context Consumer or Provider, this is the element used to fire or listen for DOM Events. So this cannot be a virtual node, it needs to be the Element that the code belongs to. But @bszyman maybe in your case it was not needed, so it worked from a compiler perspective. But it would eventually result in things not working. :-)

return this;
}
}

export default MyGlobalContext;
```
{% endcode %}

## Using Global Contexts

Once the global context extension is registered, it can be consumed in any web component throughout the backoffice.

{% code title="src/my-custom-dashboard.ts" %}
```typescript
import {
LitElement,
html,
customElement,
} from "@umbraco-cms/backoffice/external/lit";
import { UmbElementMixin } from "@umbraco-cms/backoffice/element-api";
import {
MY_GLOBAL_CONTEXT,
MyGlobalContextInterface,
} from "./service-status-context.ts";

@customElement("my-custom-dashboard")
export class MyCustomDashboard extends UmbElementMixin(LitElement) {
#myContext?: MyGlobalContextInterface;

constructor() {
super();

// Consume the global context
this.consumeContext(MY_GLOBAL_CONTEXT, (instance) => {
this.#myContext = instance;
});
}

async #handleClick() {
if (!this.#myContext) return;

// Use the context
const user = await this.#myContext.getCurrentUser();
this.#myContext.setPreference("lastVisit", new Date().toISOString());

console.log(`Welcome back, ${user}!`);
}

render() {
return html`
<uui-button @click=${this.#handleClick} look="primary">
Check User
</uui-button>
`;
}
}
```
{% endcode %}