Actions

AppDropdown

A menu of actions behind a single icon trigger — collapses a row of buttons into one tidy control.

<app-dropdown icon="more-horizontal" label="Row actions">…</app-dropdown>

<app-dropdown> hides a set of actions behind a single icon trigger — the classic “⋯” or kebab menu on a table row or card. It keeps a busy layout clean by collapsing three, four, or five actions into one control the user opens on demand.

Its look comes entirely from the shared @webority/theme, so the Razor <app-dropdown> and the React <AppDropdown> render identically — same trigger, same menu, same alignment on every Webority surface.

Playground

Every meaningful attribute, live. Each change re-renders the real tag helper on the server, so the markup underneath is the exact call that produced the menu above it.

<app-dropdown icon="three-dots" label="Row actions">…items…</app-dropdown>
toggleClassName / menuClassName have no Razor attributeBoth are React-only escape hatches for one-off styling — the Razor tag helper only exposes icon, label, align and icon-size. Style a Razor toggle by targeting .topbar-icon-btn / .wui-dropdown-trigger from CSS instead.
Menu open motion product-wide via data-wui-menu-motion — pick one, then open the menus below

Items with icons

A leading icon on each item speeds recognition. Keep them consistent — either every item has one or none does.

Icon per item
Show code
<app-dropdown icon="more-horizontal" label="Row actions">
    <button class="dropdown-item" type="button"><app-icon name="pencil"></app-icon> Edit</button>
    <button class="dropdown-item" type="button"><app-icon name="files"></app-icon> Duplicate</button>
    <button class="dropdown-item" type="button"><app-icon name="download"></app-icon> Export</button>
</app-dropdown>

Image icon

An item can lead with a picture instead of a glyph — an integration logo, a user avatar. Put an <img> (any URL, external included) inside the item; the theme sizes it to sit with the glyph items.

External logos as item icons
Show code
<app-dropdown icon="plus" label="Add integration">
    <button class="dropdown-item" type="button"><img src="https://cdn.example.com/slack.png" alt="" /> Slack</button>
    <button class="dropdown-item" type="button"><img src="https://cdn.example.com/github.png" alt="" /> GitHub</button>
</app-dropdown>
Any URL worksThe src can be an external CDN link, an uploaded avatar, or a data URI — it renders sized to match the glyph items, so a menu can mix logos and icons.

Destructive items

A delete or remove item is marked danger so it reads red, and sits last — separated from the safe actions above it.

A danger item, placed last
Show code
<app-dropdown icon="more-horizontal" label="Row actions">
    <button class="dropdown-item" type="button"><app-icon name="pencil"></app-icon> Edit</button>
    <button class="dropdown-item" type="button"><app-icon name="files"></app-icon> Duplicate</button>
    <button class="dropdown-item danger" type="button"><app-icon name="trash-2"></app-icon> Delete</button>
</app-dropdown>
Confirm before a destructive item runsA menu item is a single click away from firing. Route a delete/remove item through an AppConfirmDialog so the action can't run by accident.

Grouping with a divider

An <hr class="dropdown-divider"> renders a separator instead of an action — group related items apart from a destructive one, or separate navigation from actions.

A divider between groups
Show code
<app-dropdown icon="more-horizontal" label="Row actions">
    <button class="dropdown-item" type="button"><app-icon name="pencil"></app-icon> Edit</button>
    <button class="dropdown-item" type="button"><app-icon name="files"></app-icon> Duplicate</button>
    <hr class="dropdown-divider" />
    <button class="dropdown-item danger" type="button"><app-icon name="trash-2"></app-icon> Delete</button>
</app-dropdown>

Rich content

Nested content isn't limited to .dropdown-item buttons — put any markup inside <app-dropdown> and it renders verbatim, for an identity block, a form, anything that isn't a flat action list. The icon toggle and react-bootstrap's open/close, outside-click, focus and Escape behavior are unchanged.

An account panel
Show code
<app-dropdown icon="user" label="Account menu">
    <div class="px-3 py-2 d-flex align-items-center gap-2">
        <app-avatar name="Jane Doe"></app-avatar>
        <div>
            <div class="fw-semibold">Jane Doe</div>
            <div class="text-muted small">jane@acme.com</div>
        </div>
    </div>
    <hr class="dropdown-divider" />
    <button class="dropdown-item" type="button">Account settings</button>
    <button class="dropdown-item" type="button">Sign out</button>
</app-dropdown>
A segmented control in the menu
Rich content isn't only display — a small app-segmented turns the panel into a compact view/density picker. The dropdown keeps its open/close, outside-click and Escape behaviour.
Show code
<app-dropdown icon="settings" label="View options">
    <div class="px-3 py-2" style="min-width: 220px;">
        <div class="fw-semibold mb-2">Row density</div>
        <app-segmented label="Row density" name="density" value="cozy" size="sm" full-width="true">
            <app-segmented-option value="compact" label="Compact" />
            <app-segmented-option value="cozy" label="Cozy" />
            <app-segmented-option value="roomy" label="Roomy" />
        </app-segmented>
    </div>
</app-dropdown>
No items vs. children splitUnlike React's AppDropdown, the Razor tag helper has no items attribute — content is always the nested markup, so there's nothing to choose between. Use flat .dropdown-item buttons for a plain action list, or richer markup like this whenever you need it.

A custom trigger

Nest an <app-dropdown-toggle> slot for a rich control instead of the icon — an account chip, a labelled “Sort by” button. <app-dropdown> still owns the open/close, outside-click, focus and Escape behaviour; only the toggle's content changes.

An account chip trigger
Show code
<app-dropdown label="Account menu">
    <app-dropdown-toggle>
        <span class="d-inline-flex align-items-center gap-2">
            <app-avatar name="Jane Doe"></app-avatar>
            Jane Doe
            <app-icon name="chevron-down" size="14"></app-icon>
        </span>
    </app-dropdown-toggle>
    <button class="dropdown-item" type="button">Account settings</button>
    <button class="dropdown-item" type="button">Sign out</button>
</app-dropdown>
The toggle slot replaces the iconWhen <app-dropdown-toggle> is present it replaces the icon glyph in the toggle button, which switches to a reset wrapper (wui-dropdown-trigger) instead of the topbar icon button, so your trigger's own styling shows through unchanged.

Alignment

The menu aligns to the end of the trigger by default — right-aligned, so it never spills off the edge of a row. Switch to start when the trigger sits on the left.

end (default) vs start
Show code
<app-dropdown icon="more-horizontal" label="Aligned end" align="end">…</app-dropdown>
<app-dropdown icon="more-horizontal" label="Aligned start" align="start">…</app-dropdown>

When to use

Use it when

  • A row or card has several secondary actions that would clutter the layout as buttons.
  • You want a consistent “⋯” affordance for overflow actions across every row.
  • The actions are related and belong under one grouped trigger.

Reach for something else when

  • There is a single icon action. → AppActionIcon
  • The action is the primary thing on the screen. → AppButton
  • You are selecting one value from a list. → AppSelect
  • You need a yes/no confirmation. → AppConfirmDialog

Best practices

Do
Overflow actions collapse into one trigger, with the destructive one marked danger and placed last.
Don't
Delete sits first and unmarked — it looks like every other item and is the easiest to hit by mistake.
Do
A short, scannable list of clearly-worded actions.
Don't
A menu with nine items becomes a wall to read — group or split it, or promote the common ones out.

Every option

The whole surface of the component, one cell per value — every alignment, every toggle shape, every item flavour, and the states an ordinary example never shows: an unnamed toggle, an empty menu.

align — both values

align="end"
align="start"

toggle — icon vs. custom trigger vs. unnamed

icon="more-horizontal" (default topbar-icon-btn)
app-dropdown-toggle (wui-dropdown-trigger, icon suppressed)
label="" — unnamed toggle

item flavours

icon per item
image item (logo/avatar)
danger class item
<hr class="dropdown-divider"> (separator between groups)
<a class="dropdown-item" href="…"> (navigation item)
(empty content) — empty menu

content mode — items vs. children

flat action list
rich, non-list content

Attributes

AttributeTypeDefaultDescription
(content)markup—The menu items — one .dropdown-item element each (a <button> for an action, an <a> for navigation).
iconstringthree-dotsBootstrap Icons glyph name for the trigger button. Ignored (the icon is not rendered) when an <app-dropdown-toggle> slot is nested.
labelstringMenuNames the toggle and is its hover tooltip. Always set it. aria-label overrides it as the accessible name, exactly as in React.
alignstart | endendWhich edge of the trigger the menu aligns to.
icon-sizeint20Trigger glyph size in px (React iconSize).
aria-labelstring—Accessible name for the toggle, overriding label (React ariaLabel). With an <app-dropdown-toggle> slot the button is left unnamed unless this is set, because the trigger carries its own visible text.
toggleClassName / menuClassName——React-only; the tag helper does not expose a class override for the toggle or menu. Target .topbar-icon-btn / .wui-dropdown-trigger / .dropdown-menu from CSS instead.
linkComponent——React-only (router decoupling). Razor navigation items are always a plain <a class="dropdown-item" href="…">.
trigger——React-only prop; Razor reaches the same outcome with a nested <app-dropdown-toggle> child slot (see "A custom trigger" above).
Item shapeMenu items are inner content: a <button class="dropdown-item"> for an action, an <a class="dropdown-item"> for navigation, and add the danger class on a destructive one. A plain <hr class="dropdown-divider"> renders a separator.

Open motion (product-level)

Every menu-like overlay — dropdowns, popovers, the select/date/phone panels — shares one entrance animation from the theme. It is a product-level choice on the root element, not a per-component attribute: menus are opened by Bootstrap internals, and mixing motions inside one product reads as a bug.

Pick once, on <html><html data-wui-menu-motion="drop"> — zoom + fade + a 6px drop out of the trigger (the default) · zoom — zoom + fade only, the quietest · spring — elastic overshoot, suits marketing surfaces · unfold — scales open vertically like a sheet · none — no entrance. Menus that flip upward animate from their bottom edge automatically, and everything is disabled under prefers-reduced-motion.