Umbraco menu item active state

Hi,

I’m trying to add the active state to my tree so I’m following the code here

This works well and it adds the active attribute, but it doesn’t change when I click a new menu item. How do I change the state?

Hi @Pete0000

I’m not certain myself, but here’s a suggestion from AI which sounds plausible, but without seeing your code I can’t be certain:

uui-menu-item is purely presentational. active is just a property you set, the component never toggles it itself, so if you set it once in your markup it stays exactly where you put it. You have to own the state in the parent and re-render.

Something like:

@state()
private _activeKey?: string;

override render() {
  return html`
    ${this._items.map(item => html`
      <uui-menu-item
        label=${item.name}
        ?active=${item.key === this._activeKey}
        @click-label=${() => (this._activeKey = item.key)}>
      </uui-menu-item>`)}
  `;
}

Justin

Thanks Justin,

that really helped out. I tried to do it based on the url but it seemed to be a click behind - is there a delay in changing the url?


export class InstallerMenuItems extends UmbLitElement {
    @state()
    private _currentUrl: string;

    constructor() {
        super();
        this.#setCurrentUrl();
    }

    override render() {
        return html`
        ${this.#getMenutItem(Constants.Routes.Details.Path, Constants.Routes.Details.Label)}
        ${this.#getMenutItem(Constants.Routes.Images.Path, Constants.Routes.Images.Label)}`;
    }

    #getMenutItem(route: string, label: string): TemplateResult {
        let href = `${Constants.Routes.BaseInstallerSection}${route}`;

        return html`<uui-menu-item href="${href}" label="${label}" ?active=${this.#hasRoute(route)} @click-label=${() => this.#setCurrentUrl()}></uui-menu-item>`;
    }

    #hasRoute(route: string): boolean {
        return this._currentUrl.includes(`/${route}`);
    }

    #setCurrentUrl() {
        this._currentUrl = globalThis.location.href;
    }
}

Hi @Pete0000

AI says not to use the current href and do this instead:

There is no delay, it is just ordering. click-label fires on the click, before the router has updated history, so globalThis.location.href is still the previous URL when you read it. Hence one behind.

The fix is to stop setting the URL from the click and listen for the navigation instead:

export class InstallerMenuItems extends UmbLitElement {
    @state()
    private _currentUrl = globalThis.location.href;

    #onRouteChange = () => {
        this._currentUrl = globalThis.location.href;
    };

    override connectedCallback() {
        super.connectedCallback();
        globalThis.addEventListener('changestate', this.#onRouteChange);
        globalThis.addEventListener('popstate', this.#onRouteChange);
    }

    override disconnectedCallback() {
        globalThis.removeEventListener('changestate', this.#onRouteChange);
        globalThis.removeEventListener('popstate', this.#onRouteChange);
        super.disconnectedCallback();
    }
}

Justin

Excellent, thanks! :high-5-you-rock:

If you are using this for a section menu… then I think there are some auto active setups.. but for custom uui-menu-item as @justin-nevitech has said it’s up to up to deal with active states..
@Pete0000 maybe you could add to the docs Menu Item | CMS | Umbraco Documentation with the active implementation?

  1. Under the Hood (kind: 'link'): When you use the built-in link kind, Umbraco registers a standard menu item component that listens to router location changes. It internally compares the meta.href value against the active browser route/path. If they match, it automatically applies the active state styling to the UI element.

  2. Entity Type Matching (entityType): When you provide an entityType in the manifest meta, the backoffice menu context evaluates the current workspace or tree selection state. When the active entity type matches your definition, the menu item component automatically flags itself as active.

1. Using the Link Menu Item Kind (Recommended)

If you are adding a menu item that points to a specific route or section URL, the easiest and most idiomatic way is to use the built-in link menu item kind. Umbraco handles the active state automatically based on the current router location.

Define your menu item manifest using kind: 'link' and supply the target path in meta.href:

TypeScript

import type { ManifestMenuItem } from '@umbraco-cms/backoffice/menu';

export const menuItemManifest: ManifestMenuItem = {
  type: 'menuItem',
  kind: 'link',
  alias: 'My.MenuItem.Link',
  name: 'My Link Menu Item',
  meta: {
    label: 'Settings',
    icon: 'icon-settings',
    href: 'section/my-section/settings', // Target URL path
    menus: ['My.Menu']
  },
};

2. Associating with Workspaces and Entity Types (entityType)

If your sidebar menu item represents a content piece, entity, or workspace, you manage active states by declaring an entityType in your manifest. When the user navigates to that workspace, Umbraco matches the active entity type and highlights the corresponding menu item automatically:

TypeScript

export const menuItemManifest: ManifestMenuItem = {
  type: 'menuItem',
  alias: 'My.MenuItem.Entity',
  name: 'My Entity Menu Item',
  meta: {
    label: 'My Items',
    icon: 'icon-document',
    entityType: 'my-entity-type', // Matches your workspace entity type
    menus: ['My.Menu']
  },
};

3. Custom Menu Items (Lit Components)

If you are rendering a custom element or managing your own sidebar menu via Lit components, you control the active styling using the active attribute on Umbraco’s UI elements (such as <uui-menu-item>):

HTML

<uui-menu-item 
  label="My Custom Item" 
  icon="icon-tag" 
  ?active=${this._isActive} 
  @click=${this._handleClick}>
</uui-menu-item>

In your component logic, evaluate your active condition (e.g., listening to router changes or checking active contexts) and toggle your reactive state property (_isActive):

TypeScript

@state()
private _isActive = false;