From umbraco-cms-backoffice-skills
Implements custom UFM components in Umbraco backoffice to extend markdown rendering with syntax markers that generate dynamic HTML elements like spans or custom UI.
npx claudepluginhub umbraco/umbraco-cms-backoffice-skills --plugin umbraco-cms-backoffice-skillsThis skill uses the workspace's default tool permissions.
---
Implements Tiptap extensions for Umbraco's Rich Text Editor, adding custom nodes, marks, or capabilities via manifests and API classes using official docs.
Generates complete, testable example extensions for Umbraco backoffice using TypeScript and LitElement, runs them via Vite dev server with hot reload and mocked APIs.
Mandates invoking relevant skills via tools before any response in coding sessions. Covers access, priorities, and adaptations for Claude Code, Copilot CLI, Gemini CLI.
Share bugs, ideas, or general feedback.
UFM (Umbraco Flavored Markdown) Components extend Umbraco's markdown rendering with custom syntax. They allow you to create custom markers that transform into HTML when rendered. This is useful for creating dynamic content like localized strings, property values, or custom UI elements within markdown text. UFM components use special syntax markers (like # for localization or = for values) that get processed into HTML.
Always fetch the latest docs before implementing:
The Umbraco source includes a working example:
Location: /Umbraco-CMS/src/Umbraco.Web.UI.Client/examples/ufm-custom-component/
This example demonstrates a custom UFM component with marker syntax. Study this for production patterns.
Localization: When creating localized UFM components
umbraco-localizationContext API: When accessing data for UFM rendering
umbraco-context-apiimport type { ManifestUfmComponent } from '@umbraco-cms/backoffice/ufm';
export const manifests: Array<ManifestUfmComponent> = [
{
type: 'ufmComponent',
alias: 'My.UfmComponent.Custom',
name: 'Custom UFM Component',
api: () => import('./my-ufm-component.js'),
meta: {
alias: 'myCustom', // Usage: {myCustom:value}
marker: '%', // Optional: Short marker like {%value}
},
},
];
import { UmbUfmComponentBase } from '@umbraco-cms/backoffice/ufm';
import type { UfmToken } from '@umbraco-cms/backoffice/ufm';
export class MyUfmComponent extends UmbUfmComponentBase {
render(token: UfmToken): string | undefined {
// token.text contains the text after the marker
// e.g., {%hello} would have token.text = 'hello'
return `<span class="my-custom">${token.text}</span>`;
}
}
export { MyUfmComponent as api };
import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UmbUfmComponentBase } from '@umbraco-cms/backoffice/ufm';
import type { UfmToken } from '@umbraco-cms/backoffice/ufm';
// The UFM component that renders to custom element
export class MyUfmComponent extends UmbUfmComponentBase {
render(token: UfmToken): string | undefined {
// Output a custom element with the token text as attribute
return `<my-ufm-element text="${token.text}"></my-ufm-element>`;
}
}
// The custom element that gets rendered
@customElement('my-ufm-element')
export class MyUfmElement extends UmbLitElement {
@property()
text?: string;
override render() {
return html`<strong>${this.text}</strong>`;
}
}
export { MyUfmComponent as api };
import { UmbUfmComponentBase } from '@umbraco-cms/backoffice/ufm';
import type { UfmToken } from '@umbraco-cms/backoffice/ufm';
// Usage: {!important text here} or {highlight:important text here}
export class HighlightUfmComponent extends UmbUfmComponentBase {
render(token: UfmToken): string | undefined {
return `<mark style="background: yellow; padding: 2px 4px;">${token.text}</mark>`;
}
}
export { HighlightUfmComponent as api };
import { UmbUfmComponentBase } from '@umbraco-cms/backoffice/ufm';
import type { UfmToken } from '@umbraco-cms/backoffice/ufm';
// Usage: {@icon-document} renders an Umbraco icon
export class IconUfmComponent extends UmbUfmComponentBase {
render(token: UfmToken): string | undefined {
return `<uui-icon name="${token.text}"></uui-icon>`;
}
}
export { IconUfmComponent as api };
interface ManifestUfmComponent extends ManifestApi<UmbUfmComponentApi> {
type: 'ufmComponent';
meta: MetaUfmComponent;
}
interface MetaUfmComponent {
alias: string; // Long form: {alias:text}
marker?: string; // Short form: {marker text}
}
interface UmbUfmComponentApi extends UmbApi {
render(token: UfmToken): string | undefined;
}
interface UfmToken {
text: string; // The text content after the marker
}
{#key} or {umbLocalize:key} - Localization{=property} or {umbValue:property} - Property values{umbContentName:id} - Content names{umbLink:url} - Styled linksUFM components are used in label descriptions and markdown text:
{
"type": "propertyEditorUi",
"meta": {
"label": "{#myLabel}",
"description": "Status: {%active} - {!Important note}"
}
}
That's it! Always fetch fresh docs, keep examples minimal, generate complete working code.