Dropdown menu

Displays a menu of actions or options triggered by a button.

Installation
$Terminal
npx shadcn@latest add https://sdk-components.thesqd.com/r/dropdown-v2.json
Usage
Pass a `trigger` element and an `items` array. Drive selection with `value` / `onValueChange` — a number for single-select, or add `multiple` and use an array for multi-select.
TSImport
import { DropdownV2 } from "@/components/ui/dropdown-v2";
TSExample
import { useState } from "react";
import {
  ChevronDownIcon,
  LayoutGridIcon,
  ListIcon,
  KanbanIcon,
  CalendarIcon,
  TableIcon,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { DropdownV2 } from "@/components/ui/dropdown-v2";

const VIEWS = [
  { label: "Grid", icon: LayoutGridIcon },
  { label: "List", icon: ListIcon },
  { label: "Board", icon: KanbanIcon },
  { label: "Calendar", icon: CalendarIcon },
  { label: "Table", icon: TableIcon },
];

export function Example() {
  const [selected, setSelected] = useState(0);

  return (
    <DropdownV2
      trigger={
        <Button variant="outline">
          {`${VIEWS[selected].label} view`}
          <ChevronDownIcon />
        </Button>
      }
      items={VIEWS}
      value={selected}
      onValueChange={(v) => setSelected(v as number)}
    />
  );
}
Composition
Anatomy of the v2 compound API.
DropdownV2                          (single component — flat prop API)
├── trigger={<Button … />}          (element that opens the menu)
├── items={DropdownV2ItemSpec[] | DropdownV2Group[]}  (flat or grouped)
├── value={number | number[]}       (selected index, or indices when multiple)
├── onValueChange={(v) => …}        (v is a number, or number[] when multiple)
├── multiple                        (toggle several; menu stays open)
├── side / align / sideOffset       (positioning)
└── className="…"                   (class override for the popup panel)

DropdownV2ItemSpec (each entry in a flat items array):
├── { label, icon?, hint?, disabled?, onSelect?, closeOnClick? }
└── { label, icon?, items, search?, empty? }   (a flyout submenu; `search` filters it)

DropdownV2Group (pass an array of these for section headings + separators):
├── { heading?, items: DropdownV2ItemSpec[] }   (value indexes the flattened items)
└── { content: ReactNode }                      (rich block, e.g. a header card)

Need an always-open inline panel or a custom layout? The compound slot exports
— DropdownMenu, DropdownTrigger, DropdownContent, DropdownSub, MenuItem, plus Dropdown /
DropdownLabel / DropdownSeparator — remain available from the same file.
Single-select
Pass a `trigger` and `items`; drive selection with `value` / `onValueChange`. Proximity-hover overlays, a brand-colored selection, and a check that draws in.
Multi-select
Add `multiple` to toggle several options at once — the menu stays open while you pick and each selected row goes brand with a check.
Groups
Pass nested `{ heading, items }` groups and the menu renders section headings with a separator between groups. `value` indexes across the flattened items.
Action menu
Omit `value` / `onValueChange` and give each item an `onSelect` — the rows act as buttons (copy to clipboard, fire a toast, open a page) with no selection state. A group entry can carry rich `content` (e.g. the header card) instead of items.
Searchable submenu
Give an item its own `items` and it opens a flyout submenu — still one import, all props. Add `search` for a field across its top that narrows the rows as you type (the arrows move from the field into the rows), `empty` for what an empty one says, and `hint` for a muted note on the right of any row.
API Reference
Props for the flat `<DropdownV2>`. `DropdownMenu`, `DropdownTrigger`, `DropdownContent`, `DropdownSub` and `MenuItem` remain available as the escape hatch.

DropdownV2

PropTypeDefaultDescription
triggerReactElement—Element that opens the menu (e.g. a `<Button>`).
itemsDropdownV2ItemSpec[] | DropdownV2Group[]—A flat list of rows, or `{ heading?, items?, content? }` groups drawn with headings and separators.
value / defaultValuenumber | number[]—Selected index (single) or indices (multiple), across the flattened top-level rows. Omit for an action menu.
onValueChange(value) => void—Fires with the next selection.
multiplebooleanfalseToggle several rows; the menu stays open.
side / align / sideOffsetpositioning—Where the popup opens against the trigger.
backdropbooleanfalseDim and slightly blur the app behind the open menu — a lighter take on the Drawer's overlay. The trigger stays sharp (a hole is cut over it); a click on the backdrop closes the menu.
classNamestring—Class override for the popup panel.

DropdownV2ItemSpec (each `items` entry)

PropTypeDefaultDescription
labelstring—The row's text. Also what a submenu's `search` matches.
iconIconComponent—Optional leading icon component.
hintstring—Right-aligned muted text — a date, a count, a shortcut.
onSelect() => void—Fired when the row is chosen.
disabledboolean—Disable the row (or the submenu's trigger).
closeOnClickboolean—Override close-on-select. Default: single closes, multi stays open; submenu rows close.
itemsDropdownV2ItemSpec[]—Makes the row a flyout SUBMENU of these rows (they nest). Submenu rows are actions — each fires its own `onSelect` and closes the whole menu; they never join `value`.
searchboolean | string—Submenus only — a search field across the top that narrows the rows by `label`. Pass a string for the placeholder.
emptystring"Nothing here."Submenus only — what an empty submenu says.