Creating your own UI extension points in Umbraco v14 - Part 1: The Basics
Tags: umbraco, javascript, extensions, webdev
Published: 2024-04-02 08:00:00

In this mini series I'll take a look at a progressively more advanced example of creating your own UI extensions in Umbraco v14.

I'll loosely base this on a feature in Umbraco Commerce where we display a series of "Quick Action" buttons in a few of the editor views to provide instant access to important features. 


![Quick Action Buttons](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6usoxczmfxg7tnf10m96.png)

We provide a number of quick actions out of the box, but we also want to provide a means fore developers to add their own based on their customers needs.

> **NB** As you might expect, this is considered a fairly advanced topic for Umbraco v14 and so in the name of brevity, I assume you know a lot of fundamentals of developing for v14. If you are just getting started, you might want to go do some basic learning first before tackling this subject.

In this first post we'll take a look at the most basic implementation of a UI extension point for registering our buttons.

## Definition

The foundation of extending the UI in Umbraco v14 lies in defining custom manifests. 

Let's start by defining a basic extension manifest interface:

```javascript
export interface ManifestQuickAction extends ManifestBase {
    type: 'quickAction';
    meta: MetaQuickAction;
}

export interface MetaQuickAction  {
    label: string;
    look?: 'primary' | 'secondary';
}
```

A manifest consists of two parts. The manifest definition and a meta data object for defining custom properties we want to capture.

In this example we define a `ManifestQuickAction` manifest and give it a `type` of "quickAction". This will be how we identify our quick actions and will be used later to look up all manifests with this type.

We also define a `meta` property of type `MetaQuickAction` which at this point simply holds a `label` and optional `look` attribute which will control the text and style of our button.

In addition to the properties we've defined, we also get a number of standard manifest properties from the `ManifestBase` interface.

```javascript
export interface ManifestBase {
	type: string;
	alias: string;
	kind?: unknown;
	name: string;
	weight?: number;
}
```
These are standard for all manifests and mostly provide a means of identifying an individual manifest definition and controlling it's position.

## Implementation

With our manifest interface defined, we can now define our manifest implementations.

```javascript
export const quickActionManifests: ManifestQuickAction[] = [
    {
        type: 'quickAction',
        alias: 'Mb.QuickAction.SendEmail',
        name: 'Send Email Quick Action',
        weight: 200,
        meta: {
            label: "Send Email",
            look: "primary"
        }
    },
    {
        type: 'quickAction',
        alias: 'Mb.QuickAction.ChangeStatus',
        name: 'Change Status Quick Action',
        weight: 100,
        meta: {
            label: "Change Status"
        }
    }
]
```
## Registration

With our manifests defined we then register them with the extension registry from within our `onInit` entry point method.

```javascript
export const onInit: UmbEntryPointOnInit = (host, extensionRegistry) => {
    extensionRegistry.registerMany(quickActionManifests);
};
```
## Rendering

With all our manifests now registered, the next step is to render our buttons for each manifest.

Within the constructor of our workspace element we'll first lookup our manifest instances by querying the extensions registry.

```javascript
constructor(host: UmbControllerHost) {
    super(host);
    const quickActionsObservable = umbExtensionsRegistry.byType<string, ManifestQuickAction>('quickAction');
}
```
As with most things in Umbraco v14, what we get back from our query is an observable and so we will want to observe the collection and store it's value in a local state variable.

```javascript
@state() 
_quickActions: ManifestQuickAction[] = [];

constructor(host: UmbControllerHost) {
    super(host);
    const quickActionsObservable = umbExtensionsRegistry.byType<string, ManifestQuickAction>('quickAction'); 
    this.observe(quickActionsObservable, (manifests) => {
        this._quickActions = manifests
    })
}
```

With access to our actions, we can then output our buttons in our workspace components render method. For now we'll just output the actions label to the console when the button is clicked.

```javascript
render() {
   return html`<uui-box headline="Actions">
       ${repeat(this._quickActions,
           (qa) => qa.alias,
           (qa) => html`<uui-button 
                look=${this.manifest.meta.look ?? 'secondary'}
                @click=${() => console.log(qa.meta.label)}>
                ${qa.meta.label}
           </uui-button>`
        )}
   </uui-box>`
}
```

## What's next?

In this post I've shared the most basic example of creating a custom UI extension. If in your use case all you need is to allow developers to provide some level of configuration that you will process and handle in a predefined way, then this should cover all you need. 

If as in our example however, your configuration has a 1-to-1 relation to a rendered element (ie, our quick action button) then we can actually go a little bit further, as whilst we have allowed developers to define their own quick action buttons, the implementation of what a quick action is and does is really rather restrictive.

In the next few posts in this series I'll take a look at how we can make this even more flexible by allowing developers to change the buttons behavior when clicked and also how they could swap out the button component entirely.

Until next time 👋