Create a new extension kind — a reusable default implementation for an extension type. Use when multiple extensions of the same type share the same element/API implementation and only differ in configuration. Kinds reduce boilerplate by providing pre-built defaults that extensions customize via meta.
Create a new extension kind — a reusable default implementation for an extension type.
type this kind applies to (e.g., 'entityAction', 'section')kind string (e.g., 'delete', 'default', 'button')Create a kind when multiple extensions of the same type share the same element/API implementation and only differ in meta configuration. If the implementation is unique, a kind adds unnecessary indirection.
my-feature/
├── kinds/
│ └── my-kind/
│ ├── my-kind.kind.ts # Kind manifest definition
│ ├── my-kind.element.ts # Default element (if providing UI)
│ ├── my-kind.api.ts # Default API class (if providing logic)
│ └── manifests.ts # Exports the kind manifest
// my-kind.kind.ts
import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry';
export const UMB_MY_FEATURE_MY_KIND_MANIFEST: UmbExtensionManifestKind = {
type: 'kind',
alias: 'Umb.Kind.MyFeature.MyKind',
matchType: 'myFeature',
matchKind: 'myKind',
manifest: {
type: 'myFeature',
kind: 'myKind',
element: () => import('./my-kind.element.js'),
api: () => import('./my-kind.api.js'),
weight: 1000,
meta: {
icon: '',
label: '',
// ... defaults that consumers override
},
},
};
export const manifest = UMB_MY_FEATURE_MY_KIND_MANIFEST;
type: Always 'kind'alias: Unique identifier. Convention: Umb.Kind.{ExtensionType}.{KindName}matchType: The extension type this kind applies tomatchKind: The kind name that extensions referencemanifest: Partial manifest with defaults — any property here becomes the base for extensions using this kind// my-kind.element.ts
import { customElement, html } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
@customElement('umb-my-kind-element')
export class UmbMyKindElement extends UmbLitElement {
// This element is shared by all extensions using this kind.
// Use manifest meta for customization — not hardcoded values.
override render() {
return html`<!-- default rendering -->`;
}
}
export { UmbMyKindElement as element };
Important: Export as element (named export) — the lazy import() in the kind manifest resolves this name.
// my-kind.api.ts
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
export class UmbMyKindApi extends UmbControllerBase {
// Shared logic for all extensions using this kind.
// Access extension-specific config via the manifest meta.
}
export { UmbMyKindApi as api };
// manifests.ts
import { manifest as myKindManifest } from './my-kind.kind.js';
import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry';
export const manifests: Array<UmbExtensionManifest | UmbExtensionManifestKind> = [myKindManifest];
Add this to the parent feature's manifests.ts so it bubbles up to the bundle.
In the extension type definition file, add a variant interface for this kind:
export interface ManifestMyFeatureMyKind extends ManifestMyFeature {
type: 'myFeature';
kind: 'myKind';
meta: MetaMyFeatureMyKind;
}
export interface MetaMyFeatureMyKind extends MetaMyFeature {
icon: string;
label: string;
}
// Update the global declaration to include the kind variant
declare global {
interface UmbExtensionManifestMap {
umbMyFeature: ManifestMyFeature | ManifestMyFeatureMyKind;
}
}
Extensions reference the kind and only specify what differs:
{
type: 'myFeature',
kind: 'myKind',
alias: 'My.Package.MyFeature.Specific',
name: 'My Specific Feature',
meta: {
icon: 'icon-heart',
label: 'My Label',
},
}
The registry merges the kind defaults with the extension manifest:
meta is shallow-merged (extension meta extends kind meta, doesn't replace it)A kind can build on another kind by spreading its manifest:
import { UMB_MY_FEATURE_DEFAULT_KIND_MANIFEST } from '../default/default.kind.js';
export const manifest: UmbExtensionManifestKind = {
type: 'kind',
alias: 'Umb.Kind.MyFeature.Special',
matchType: 'myFeature',
matchKind: 'special',
manifest: {
...UMB_MY_FEATURE_DEFAULT_KIND_MANIFEST.manifest,
type: 'myFeature',
kind: 'special',
api: () => import('./special.api.js'),
meta: {
...UMB_MY_FEATURE_DEFAULT_KIND_MANIFEST.manifest.meta,
// additional meta defaults
},
},
};
type: 'kind' with unique aliasmatchType matches the target extension type exactlymatchKind matches the kind string extensions will useelement, default API exported as api (named exports for lazy import)declare global blockmanifests.ts with type Array<UmbExtensionManifest | UmbExtensionManifestKind>npm run compile