|
| 1 | +--- |
| 2 | +description: >- |
| 3 | + Create menu items that appear throughout the backoffice, in sidebars, button flyouts, and more. |
| 4 | +--- |
| 5 | + |
| 6 | +# Menu Items |
| 7 | + |
| 8 | +Menu Item extensions are used in combination with [Menu](menu.md) extensions. Menu items can be placed in custom menus, sidebars, and even added into the built-in/default Umbraco menu extensions. Extension authors can use the default Menu Item component or create their own custom Menu Items and register them as extensions. |
| 9 | + |
| 10 | +<figure><img src="../../../.gitbook/assets/menu-item.png" alt="" width="250"><figcaption><p>Menu Item</p></figcaption></figure> |
| 11 | + |
| 12 | +Menu Item extensions can be defined using either JSON in `umbraco-package.json` or using TypeScript. |
| 13 | + |
| 14 | +## Creating Menu Items |
| 15 | + |
| 16 | +Menu Item extensions can be created using either JSON or TypeScript. Both approaches are shown below. |
| 17 | + |
| 18 | +### Manifest |
| 19 | + |
| 20 | +To add custom menu items, you can define a single MenuItem manifest and link an element to it. In this element, you can fetch the data and render as many menu items as you want based on that data. |
| 21 | + |
| 22 | +{% tabs %} |
| 23 | +{% tab title="JSON" %} |
| 24 | +{% code title="umbraco-package.json" %} |
| 25 | +```json |
| 26 | +{ |
| 27 | + "$schema": "../../umbraco-package-schema.json", |
| 28 | + "name": "My Package", |
| 29 | + "version": "0.1.0", |
| 30 | + "extensions": [ |
| 31 | + { |
| 32 | + "type": "menuItem", |
| 33 | + "alias": "My.MenuItem", |
| 34 | + "name": "My Menu Item", |
| 35 | + "element": "./menu-items.ts", |
| 36 | + "meta": { |
| 37 | + "label": "My Menu Item", |
| 38 | + "menus": ["My.Menu"] |
| 39 | + } |
| 40 | + } |
| 41 | + ] |
| 42 | +} |
| 43 | +``` |
| 44 | +{% hint style="info" %} |
| 45 | +The `element` parameter is optional. Omitting it will render a menu item styled using Umbraco defaults. |
| 46 | +{% endhint %} |
| 47 | +{% endcode %} |
| 48 | +{% endtab %} |
| 49 | +{% tab title="TypeScript" %} |
| 50 | + |
| 51 | +Extension authors define the menu manifest, then register it dynamically/during runtime using a [Backoffice Entry Point](../../extending-overview/extension-types/backoffice-entry-point.md) extension. |
| 52 | + |
| 53 | +The `element` attribute will point toward a custom Lit component, an example of which will be in the next section of this article. |
| 54 | + |
| 55 | +{% code title="my-menu/manifests.ts" %} |
| 56 | +```typescript |
| 57 | +import type { ManifestMenuItem } from '@umbraco-cms/backoffice/menu'; |
| 58 | + |
| 59 | +export const menuItemManifest: ManifestMenuItem = { |
| 60 | + type: 'menuItem', |
| 61 | + alias: 'My.MenuItem', |
| 62 | + name: 'My Menu Item', |
| 63 | + meta: { |
| 64 | + label: 'My Menu Item', |
| 65 | + menus: ["My.Menu"] |
| 66 | + }, |
| 67 | +}; |
| 68 | +``` |
| 69 | +{% endcode %} |
| 70 | + |
| 71 | +{% code title="entrypoints/entrypoints.ts" %} |
| 72 | +```typescript |
| 73 | +import type { |
| 74 | + UmbEntryPointOnInit, |
| 75 | +} from "@umbraco-cms/backoffice/extension-api"; |
| 76 | +import { umbExtensionsRegistry } from "@umbraco-cms/backoffice/extension-registry"; |
| 77 | +import { menuItemManifest } from "./../my-menu/manifests.ts"; |
| 78 | + |
| 79 | +export const onInit: UmbEntryPointOnInit = (_host, _extensionRegistry) => { |
| 80 | + console.log("Hello from my extension 🎉"); |
| 81 | + |
| 82 | + umbExtensionsRegistry.register(menuItemManifest); |
| 83 | +}; |
| 84 | +``` |
| 85 | +{% endcode %} |
| 86 | +{% endtab %} |
| 87 | +{% endtabs %} |
| 88 | + |
| 89 | +## Custom menu items |
| 90 | + |
| 91 | +{% hint style="info" %} |
| 92 | +**Note:** Displaying menu item extensions does not require extension authors to create custom menu item subclasss. This step is optional. |
| 93 | +{% endhint %} |
| 94 | + |
| 95 | +To render your menu items in Umbraco, extension authors can use the [Umbraco UI Menu Item component](https://uui.umbraco.com/?path=/docs/uui-menu-item--docs). This component enables nested menu structures with a few lines of markup. |
| 96 | + |
| 97 | +`<uui-menu-item>` nodes accept the `has-children` boolean attribute, which will display a caret icon indicating nested items. Tying this boolean attribute to a variable requires using the `?` Lit directive, which would look similar to this: `?has-children=${boolVariable}`. |
| 98 | + |
| 99 | +```html |
| 100 | +<uui-menu-item label="Menu Item 1" has-children> |
| 101 | + <uui-menu-item label="Nested Menu Item 1"></uui-menu-item> |
| 102 | + <uui-menu-item label="Nested Menu Item 2"></uui-menu-item> |
| 103 | +</uui-menu-item> |
| 104 | +``` |
| 105 | + |
| 106 | +### Custom menu item element example |
| 107 | + |
| 108 | +Custom elements can fetch the data and render menu items using markup, like above. Storing the results of the fetch in a `@state()` variable will trigger a re-render of the component when the value of the variable changes. |
| 109 | + |
| 110 | +{% tabs %} |
| 111 | +{% tab title="my-menu/menu-items.ts" %} |
| 112 | +{% code title="menu-items.ts" overflow="wrap" lineNumbers="true" %} |
| 113 | +```typescript |
| 114 | +import type { UmbMenuItemElement } from '@umbraco-cms/backoffice/menu'; |
| 115 | +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; |
| 116 | +import { html, TemplateResult, customElement, state } from '@umbraco-cms/backoffice/external/lit'; |
| 117 | +import { MyMenuItemResponseModel, MyMenuResource } from '../../../api'; |
| 118 | + |
| 119 | +const elementName = 'my-menu-item'; |
| 120 | + |
| 121 | +@customElement(elementName) |
| 122 | +class MyMenuItems extends UmbLitElement implements UmbMenuItemElement { |
| 123 | + @state() |
| 124 | + private _items: MyMenuItemResponseModel[] = []; // Store fetched items |
| 125 | + |
| 126 | + @state() |
| 127 | + private _loading: boolean = true; // Track loading state |
| 128 | + |
| 129 | + @state() |
| 130 | + private _error: string | null = null; // Track any errors |
| 131 | + |
| 132 | + override firstUpdated() { |
| 133 | + this.fetchInitialItems(); // Start fetching on component load |
| 134 | + } |
| 135 | + |
| 136 | + // Fetch initial items |
| 137 | + async fetchInitialItems() { |
| 138 | + try { |
| 139 | + this._loading = true; |
| 140 | + this._items = ((await MyMenuResource.getMenuApiV1()).items); // Fetch root-level items |
| 141 | + } catch (e) { |
| 142 | + this._error = 'Error fetching items'; |
| 143 | + } finally { |
| 144 | + this._loading = false; |
| 145 | + } |
| 146 | + } |
| 147 | + |
| 148 | + // Render items |
| 149 | + renderItems(items: MyMenuItemResponseModel[]): TemplateResult { |
| 150 | + return html` |
| 151 | + ${items.map(element => html` |
| 152 | + <uui-menu-item label="${element.name}" ?has-children=${element.hasChildren}> |
| 153 | + ${element.type === 1 |
| 154 | + ? html`<uui-icon slot="icon" name="icon-folder"></uui-icon>` |
| 155 | + : html`<uui-icon slot="icon" name="icon-autofill"></uui-icon>`} |
| 156 | + <!-- recursively render children --> |
| 157 | + ${element.hasChildren ? this.renderItems(element.children) : ''} |
| 158 | + </uui-menu-item> |
| 159 | + `)} |
| 160 | + `; |
| 161 | + } |
| 162 | + |
| 163 | + // Main render function |
| 164 | + override render() { |
| 165 | + if (this._loading) { |
| 166 | + return html`<uui-loader></uui-loader>`; |
| 167 | + } |
| 168 | + |
| 169 | + if (this._error) { |
| 170 | + return html`<uui-menu-item active disabled label="Could not load form tree!"> |
| 171 | + </uui-menu-item>`; |
| 172 | + } |
| 173 | + |
| 174 | + // Render items if loading is done and no error occurred |
| 175 | + return this.renderItems(this._items); |
| 176 | + } |
| 177 | +} |
| 178 | + |
| 179 | +export { MyMenuItems as element }; |
| 180 | + |
| 181 | +declare global { |
| 182 | + interface HTMLElementTagNameMap { |
| 183 | + [elementName]: MyMenuItems; |
| 184 | + } |
| 185 | +} |
| 186 | +``` |
| 187 | +{% endcode %} |
| 188 | +{% endtab %} |
| 189 | + |
| 190 | +{% tab title="my-menu/manifests.ts" %} |
| 191 | +```typescript |
| 192 | +import type { ManifestMenuItem } from "@umbraco-cms/backoffice/menu"; |
| 193 | + |
| 194 | +export const MyMenuItemManifest: ManifestMenuItem = { |
| 195 | + type: "menuItem", |
| 196 | + kind: "tree", |
| 197 | + alias: "MyMenuItem.CustomMenu?Item", |
| 198 | + name: "My Menu Item Custom Menu Item", |
| 199 | + element: () => import("./menu-items.ts"), |
| 200 | + meta: { |
| 201 | + label: "Smtp", |
| 202 | + menus: ["Umb.Menu.Content"], |
| 203 | + }, |
| 204 | +}; |
| 205 | +``` |
| 206 | +{% hint style="info" %} |
| 207 | +**Note:** Extension authors can use the `kind` property to define the type of menu item. The `kind` property can be set to `tree` or `list`. |
| 208 | +{% endhint %} |
| 209 | +{% endtab %} |
| 210 | + |
| 211 | +{% tab title="entrypoints/entrypoints.ts" %} |
| 212 | +```typescript |
| 213 | +import type { |
| 214 | + UmbEntryPointOnInit, |
| 215 | +} from "@umbraco-cms/backoffice/extension-api"; |
| 216 | +import { umbExtensionsRegistry } from "@umbraco-cms/backoffice/extension-registry"; |
| 217 | +import { MyMenuItemManifest } from "./../my-menu/manifests.ts"; |
| 218 | + |
| 219 | +export const onInit: UmbEntryPointOnInit = (_host, _extensionRegistry) => { |
| 220 | + console.log("Hello from my extension 🎉"); |
| 221 | + |
| 222 | + umbExtensionsRegistry.register(MyMenuItemManifest); |
| 223 | +}; |
| 224 | +``` |
| 225 | +{% endtab %} |
| 226 | +{% endtabs %} |
| 227 | + |
| 228 | +## Adding menu items to an existing menu |
| 229 | + |
| 230 | +Extension authors are able to add their own additional menu items to the menus that ship with Umbraco. |
| 231 | + |
| 232 | +Some examples of these built-in menus include: |
| 233 | + |
| 234 | +* Content - `Umb.Menu.Content` |
| 235 | +* Media - `Umb.Menu.Media` |
| 236 | +* Settings - `Umb.Menu.StructureSettings` |
| 237 | +* Templating - `Umb.Menu.Templating` |
| 238 | +* ... |
| 239 | + |
| 240 | +Additional Umbraco menus (nine, total) can be found using the Extension Insights browser and selecting **Menu** from the dropdown. |
| 241 | + |
| 242 | +<figure><img src="../../../.gitbook/assets/extension-types-backoffice-browser.png" alt=""><figcaption><p>Backoffice extension browser</p></figcaption></figure> |
| 243 | + |
| 244 | +### Extending Menus |
| 245 | + |
| 246 | +To add a menu item to an existing menu, use the `meta.menus` property. |
| 247 | + |
| 248 | +{% code title="umbraco-package.json" %} |
| 249 | +```json |
| 250 | +{ |
| 251 | + "$schema": "../../umbraco-package-schema.json", |
| 252 | + "name": "My Package", |
| 253 | + "version": "0.1.0", |
| 254 | + "extensions": [ |
| 255 | + { |
| 256 | + "type": "menuItem", |
| 257 | + "alias": "My.MenuItem", |
| 258 | + "name": "My Menu Item", |
| 259 | + "meta": { |
| 260 | + "label": "My Menu Item", |
| 261 | + "menus": ["Umb.Menu.Content"] |
| 262 | + }, |
| 263 | + "element": "menu-items.js" |
| 264 | + } |
| 265 | + ] |
| 266 | +} |
| 267 | +``` |
| 268 | +{% endcode %} |
0 commit comments