Skip to content

Dropdown

Dropdown menu component with customizable trigger. Supports controlled and uncontrolled state, portal rendering, and automatic vertical repositioning.

Also known as: popover, popup menu, context menu, action menu, overflow menu

View in Storybook

When to use

  • Custom dropdown menus triggered by any element. For selecting options from a list, use Select.
  • For navigation, combine with Button.

Import

tsx
import { Dropdown } from '@vacano/ui'

Usage

tsx
<Dropdown trigger={<Button>Open Menu</Button>}>
  <div style={{ padding: 8 }}>
    <p>Menu item 1</p>
    <p>Menu item 2</p>
    <p>Menu item 3</p>
  </div>
</Dropdown>

Alignment

tsx
// Left aligned (default)
<Dropdown align="left" trigger={<Button>Left</Button>}>
  <p>Left-aligned dropdown</p>
</Dropdown>

// Right aligned
<Dropdown align="right" trigger={<Button>Right</Button>}>
  <p>Right-aligned dropdown</p>
</Dropdown>

Controlled State

tsx
const [open, setOpen] = useState(false)

<Dropdown
  open={open}
  onOpen={() => setOpen(true)}
  onClose={() => setOpen(false)}
  trigger={<Button>Controlled</Button>}
>
  <Button onClick={() => setOpen(false)}>Close</Button>
</Dropdown>

Portal Rendering

For use inside containers with overflow: hidden:

tsx
<Dropdown
  trigger={<Button>With Portal</Button>}
  portalRenderNode={document.body}
>
  <p>Dropdown content</p>
</Dropdown>

Auto Close

By default, the dropdown closes when clicking inside the dropdown content. Set autoClose={false} to keep it open:

tsx
// Default: closes on click (autoClose=true)
<Dropdown trigger={<Button>Auto Close</Button>}>
  <button onClick={() => alert('Clicked!')}>Action</button>
</Dropdown>

// Stays open on click
<Dropdown autoClose={false} trigger={<Button>Stay Open</Button>}>
  <button onClick={() => alert('Clicked!')}>Action</button>
</Dropdown>
tsx
function MenuDropdown() {
  return (
    <Dropdown trigger={<Button>Actions</Button>}>
      <div style={{ minWidth: 150 }}>
        <button style={{ display: 'block', width: '100%' }}>
          Edit
        </button>
        <button style={{ display: 'block', width: '100%' }}>
          Duplicate
        </button>
        <button style={{ display: 'block', width: '100%', color: 'red' }}>
          Delete
        </button>
      </div>
    </Dropdown>
  )
}

Props

PropTypeDefaultDescription
triggerReactElementrequiredTrigger element that toggles the dropdown on click
childrenReactNode-Dropdown content
align'left' | 'right''left'Horizontal alignment of dropdown relative to trigger
autoClosebooleantrueClose dropdown when clicking inside the dropdown content
openboolean-Controlled open state. When provided, component becomes controlled.
onOpen() => void-Callback fired when dropdown opens
onClose() => void-Callback fired when dropdown closes
portalRenderNodeHTMLElement | null-When provided, dropdown content is rendered via createPortal into this node
classNamestring-CSS class name for the root container
classnamesDropdownClassNames-Custom class names for sub-elements
refRef<HTMLDivElement>-Ref forwarded to the root container
data-test-idstring-Test identifier attribute
KeyDescription
triggerTrigger wrapper element
contentDropdown content container
  • Select - Option selection dropdown
  • Button - Common trigger element
  • Tooltip - Hover information popup

Released under the MIT License.