Menu

SvMenu: a dropdown / actions menu with submenus, separators, icons and keyboard shortcuts. Portalled + animated, full keyboard (arrows, Enter, Escape, ArrowRight/Left).

A live, editable Svelte 5 component example from the SvGrid gallery (Layout). Read the SvGrid UI component docs for the full API.

What this example shows

SvMenu - a dropdown / actions menu with submenus, separators, icons and keyboard shortcuts. Portalled + animated; full keyboard (arrows, Enter, Escape, ArrowRight/Left for submenus).

Imports, features and API used

Imports: @svgrid/grid

Source code (326-menu.svelte)

<script lang="ts">
  /**
   * SvMenu - a dropdown / actions menu with submenus, separators, icons and
   * keyboard shortcuts. Portalled + animated; full keyboard (arrows, Enter,
   * Escape, ArrowRight/Left for submenus).
   */
  import { SvMenu, SvButton } from '@svgrid/grid'
  import type { MenuItem } from '@svgrid/grid'

  let last = $state('')

  const items: MenuItem[] = [
    { label: 'New file', shortcut: 'Ctrl+N', onSelect: () => (last = 'New file') },
    { label: 'Open...', shortcut: 'Ctrl+O', onSelect: () => (last = 'Open') },
    { separator: true },
    {
      label: 'Share',
      children: [
        { label: 'Copy link', onSelect: () => (last = 'Copy link') },
        { label: 'Email', onSelect: () => (last = 'Email') },
        { separator: true },
        { label: 'Export as PDF', onSelect: () => (last = 'Export PDF') },
      ],
    },
    { label: 'Rename', shortcut: 'F2', onSelect: () => (last = 'Rename') },
    { separator: true },
    { label: 'Delete', shortcut: 'Del', onSelect: () => (last = 'Delete') },
    { label: 'Archived (disabled)', disabled: true },
  ]
</script>

<div class="wrap">
  <header>
    <h2>Menu</h2>
    <p>A dropdown menu with submenus, separators, shortcuts and full keyboard. Arrow to navigate, Enter to pick, ArrowRight opens a submenu.</p>
  </header>

  <div class="row">
    <SvMenu {items}>
      {#snippet anchor()}<SvButton variant="primary">File actions ▾</SvButton>{/snippet}
    </SvMenu>

    <SvMenu {items} ariaLabel="Row actions">
      {#snippet anchor()}<SvButton variant="outline">⋯</SvButton>{/snippet}
    </SvMenu>

    <span class="last">{last ? `Chose: ${last}` : 'Nothing chosen yet'}</span>
  </div>
</div>

<style>
  .wrap { padding: 20px; max-width: 620px; display: flex; flex-direction: column; gap: 18px; }
  header h2 { margin: 0 0 4px; font-size: 20px; font-weight: 700; }
  header p { margin: 0; color: var(--sg-muted, #64748b); font-size: 13.5px; line-height: 1.5; }
  .row { display: flex; align-items: center; gap: 14px; }
  .last { font-size: 13px; color: var(--sg-muted, #64748b); }
</style>

View this example on GitHub

Related documentation

Related articles

More Layout examples

  • Account & security settings console - A real SaaS settings surface composed from the UI kit: SvMenubar app bar, promise + Undo-action toasts on save, SvPopconfirm on destructive rows, SvHoverCard teammate previews, and frame input adornments (leading icons, prefix affixes, masked API key). Profile / Team / Security tabs over SvCard + SvStat.
  • Invoice builder - An adornment-heavy money form: currency prefixes, % suffixes, masked tax IDs (frame adornments), line items with SvPopconfirm delete + Undo action toasts, live SvStat totals, an SvHoverCard tax hint, and a promise toast on Send. Pure UI-kit composition.
  • Operations KPI dashboard - A KPI console: SvStat + SvSparkline tiles, an SvGauge SLA dial, SvHoverCard drill-down previews per service, an SvMenubar toolbar (View / Range / Actions) and a promise toast on Refresh. Pure UI-kit composition, no grid dependency.
  • Form: rich field types - SvForm reaching the whole input suite from one schema - phone, country, mask, combobox, radio, slider, tags, datetime and file - plus per-field help text, a readonly field, column span, and a promise toast on submit.
  • Form: cascading fields - SvForm dependent selects - a child list derives from the parent value via a function `options`, and `dependsOn` clears the child when the parent changes so a stale selection never lingers. Country -> State -> City.