ComponentsDropdownMenu

DropdownMenu

Display a dropdown menu.

Documentation

Usage

Use the DropdownMenu, DropdownMenuTrigger, and DropdownMenuContent components to build out menus.

import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@optiaxiom/react";
import { IconLogout, IconUser } from "@tabler/icons-react";
 
export function App() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger>Open</DropdownMenuTrigger>
 
      <DropdownMenuContent>
        <DropdownMenuLabel>My Account</DropdownMenuLabel>
        <DropdownMenuItem icon={<IconUser />}>View Profile</DropdownMenuItem>
        <DropdownMenuSeparator />
        <DropdownMenuItem icon={<IconLogout />}>Logout</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Controlled

Use the open and defaultOpen props to toggle between controlled and uncontrolled usage. And combine it with onOpenChange to listen for changes to the state.

import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
  Flex,
  Switch,
} from "@optiaxiom/react";
import { IconLogout, IconUser } from "@tabler/icons-react";
import { useState } from "react";
 
export function App() {
  const [keepOpen, setKeepOpen] = useState(false);
  const [open, setOpen] = useState(false);
 
  return (
    <Flex flexDirection="row">
      <Switch onCheckedChange={setKeepOpen}>Keep menu open</Switch>
 
      <DropdownMenu
        modal={false}
        onOpenChange={(flag) => setOpen(flag || keepOpen)}
        open={open}
      >
        <DropdownMenuTrigger>Open</DropdownMenuTrigger>
 
        <DropdownMenuContent>
          <DropdownMenuLabel>My Account</DropdownMenuLabel>
          <DropdownMenuItem icon={<IconUser />}>View Profile</DropdownMenuItem>
          <DropdownMenuSeparator />
          <DropdownMenuItem icon={<IconLogout />}>Logout</DropdownMenuItem>
        </DropdownMenuContent>
      </DropdownMenu>
    </Flex>
  );
}

Non modal usage

By default scrolling and interactions with outside elements is disabled when menu is open. Set modal prop to false in order to render a non-modal dropdown.

import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@optiaxiom/react";
import { IconLogout, IconUser } from "@tabler/icons-react";
 
export function App() {
  return (
    <DropdownMenu modal={false}>
      <DropdownMenuTrigger>Open</DropdownMenuTrigger>
 
      <DropdownMenuContent>
        <DropdownMenuLabel>My Account</DropdownMenuLabel>
        <DropdownMenuItem icon={<IconUser />}>View Profile</DropdownMenuItem>
        <DropdownMenuSeparator />
        <DropdownMenuItem icon={<IconLogout />}>Logout</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Trigger

Customize trigger

By default we use the Button component for the menu trigger which accepts all of the existing button props.

import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@optiaxiom/react";
import { IconDots, IconLogout, IconUser } from "@tabler/icons-react";
 
export function App() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger
        appearance="subtle"
        aria-label="My Account"
        icon={<IconDots />}
      />
 
      <DropdownMenuContent>
        <DropdownMenuLabel>My Account</DropdownMenuLabel>
        <DropdownMenuItem icon={<IconUser />}>View Profile</DropdownMenuItem>
        <DropdownMenuSeparator />
        <DropdownMenuItem icon={<IconLogout />}>Logout</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Content

Menu position

Use the align and side props on DropdownMenuContent to change the default position of the dropdown.

import type { ComponentPropsWithRef } from "react";
 
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@optiaxiom/react";
import { IconLogout, IconUser } from "@tabler/icons-react";
 
export function App({
  align,
  side,
}: Pick<ComponentPropsWithRef<typeof DropdownMenuContent>, "align" | "side">) {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger>Open</DropdownMenuTrigger>
 
      <DropdownMenuContent align={align} side={side}>
        <DropdownMenuLabel>My Account</DropdownMenuLabel>
        <DropdownMenuItem icon={<IconUser />}>View Profile</DropdownMenuItem>
        <DropdownMenuSeparator />
        <DropdownMenuItem icon={<IconLogout />}>Logout</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Item

Appearance

Use the intent prop to control the appearance of items.

import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@optiaxiom/react";
import { IconPencil, IconTrash } from "@tabler/icons-react";
 
export function App() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger>Open</DropdownMenuTrigger>
 
      <DropdownMenuContent>
        <DropdownMenuItem icon={<IconPencil />}>Edit</DropdownMenuItem>
        <DropdownMenuItem icon={<IconTrash />} intent="danger">
          Delete
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Description

Use the description prop to add secondary text to items.

import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@optiaxiom/react";
 
export function App() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger>Actions</DropdownMenuTrigger>
 
      <DropdownMenuContent>
        <DropdownMenuItem description="Create a new task">
          New task
        </DropdownMenuItem>
        <DropdownMenuItem description="Copy this task">
          Copy task
        </DropdownMenuItem>
        <DropdownMenuSeparator />
        <DropdownMenuItem intent="danger">Delete task</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Disabled items

Use the disabled prop on menu items to disable them.

import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@optiaxiom/react";
import { IconLogout, IconUser, IconUsers } from "@tabler/icons-react";
 
export function App() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger>Open</DropdownMenuTrigger>
 
      <DropdownMenuContent>
        <DropdownMenuLabel>My Account</DropdownMenuLabel>
        <DropdownMenuItem icon={<IconUser />}>View Profile</DropdownMenuItem>
        <DropdownMenuItem disabled icon={<IconUsers />}>
          Team Management
        </DropdownMenuItem>
        <DropdownMenuSeparator />
        <DropdownMenuItem icon={<IconLogout />}>Logout</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Selection

Use the onSelect prop to handle selection of items.

Last selected item:

import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
  Flex,
  Text,
} from "@optiaxiom/react";
import { IconLogout, IconUser } from "@tabler/icons-react";
import { useState } from "react";
 
export function App() {
  const [selected, setSelected] = useState("");
 
  return (
    <Flex flexDirection="row">
      <DropdownMenu>
        <DropdownMenuTrigger>Open</DropdownMenuTrigger>
 
        <DropdownMenuContent>
          <DropdownMenuLabel>My Account</DropdownMenuLabel>
          <DropdownMenuItem
            icon={<IconUser />}
            onSelect={() => setSelected("view")}
          >
            View Profile
          </DropdownMenuItem>
          <DropdownMenuSeparator />
          <DropdownMenuItem
            icon={<IconLogout />}
            onSelect={() => setSelected("logout")}
          >
            Logout
          </DropdownMenuItem>
        </DropdownMenuContent>
      </DropdownMenu>
 
      <Text>Last selected item: {selected}</Text>
    </Flex>
  );
}

Left and right addons

Use the icon, addonBefore, and addonAfter props to add icons, text, or any other element to the start or end of the menu items.

import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
  Kbd,
} from "@optiaxiom/react";
import { IconLogout, IconSettings, IconUser } from "@tabler/icons-react";
 
export function App() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger>Open</DropdownMenuTrigger>
 
      <DropdownMenuContent>
        <DropdownMenuLabel>My Account</DropdownMenuLabel>
        <DropdownMenuItem
          addonAfter={
            <Kbd keys={["option", "shift"]} variant="subtle">
              P
            </Kbd>
          }
          icon={<IconSettings />}
        >
          Preferences
        </DropdownMenuItem>
        <DropdownMenuItem icon={<IconUser />}>View Profile</DropdownMenuItem>
        <DropdownMenuSeparator />
        <DropdownMenuItem
          addonAfter={
            <Kbd keys={["option", "shift"]} variant="subtle">
              Q
            </Kbd>
          }
          icon={<IconLogout />}
        >
          Logout
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Label

Use the DropdownMenuLabel component to render a read-only label.

import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@optiaxiom/react";
import { IconLogout, IconUser } from "@tabler/icons-react";
 
export function App() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger>Open</DropdownMenuTrigger>
 
      <DropdownMenuContent>
        <DropdownMenuLabel>My Account</DropdownMenuLabel>
        <DropdownMenuItem icon={<IconUser />}>View Profile</DropdownMenuItem>
        <DropdownMenuSeparator />
        <DropdownMenuItem icon={<IconLogout />}>Logout</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Dialogs inside menus

Combine dropdown menus with dialog to show alerts when selecting an item.

Make sure to enable asChild prop on AlertDialogTrigger and call event.preventDefault() in the item onSelect handler.

import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogBody,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTrigger,
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@optiaxiom/react";
import { IconPencil, IconTrash } from "@tabler/icons-react";
 
export function App() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger>Open</DropdownMenuTrigger>
 
      <DropdownMenuContent>
        <DropdownMenuItem icon={<IconPencil />}>Edit</DropdownMenuItem>
        <AlertDialog>
          <AlertDialogTrigger asChild>
            <DropdownMenuItem
              icon={<IconTrash />}
              intent="danger"
              onSelect={(event) => event.preventDefault()}
            >
              Delete
            </DropdownMenuItem>
          </AlertDialogTrigger>
 
          <AlertDialogContent>
            <AlertDialogHeader>Are you sure?</AlertDialogHeader>
            <AlertDialogBody>
              The task and all content will be deleted.
            </AlertDialogBody>
            <AlertDialogFooter>
              <AlertDialogCancel />
              <AlertDialogAction>Yes, delete</AlertDialogAction>
            </AlertDialogFooter>
          </AlertDialogContent>
        </AlertDialog>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Related

Autocomplete

Text field with inline autocomplete to show suggestions while allowing freeform user input.

Select

Single select combobox widget to allow selection from a fixed set of options.

Props

DropdownMenu

Doesn't render its own HTML element.

Prop

defaultOpen

The initial open state in uncontrolled mode.

false | true

modal

When enabled interaction with outside elements will be disabled and only dropdown content will be visible to screen readers.

false | true

onOpenChange

Handler that is called when the open state changes.

(open: boolean) => void

open

The open state in controlled mode.

false | true

DropdownMenuTrigger

Supports all Button props in addition to its own. Renders a <button> element.

Prop

addonAfter

Display content inside the button after children.

ReactNode

addonBefore

Display content inside the button before children.

ReactNode

appearance

Control the appearance by selecting between the different button types.

"default" | "danger" | "primary" | "subtle" | "danger-outline" | "inverse"

asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

false | true

className

string

disabled

Whether the button is disabled.

false | true

icon

Display an icon before or after the button content or omit children to only show the icon.

ReactNode

iconOnly

Whether button should have square shape.

false | true

iconPosition

Control whether to show the icon before or after the button content.

"end" | "start"

loading

Whether to show loading spinner inside the button.

false | true

size

Control the size of the button.

"sm" | "md" | "lg"

DropdownMenuContent

Supports all Box props in addition to its own. Renders a <div> element.

Prop

align

"center" | "end" | "start"

Default: start

asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

false | true

className

string

loading

Whether to show loading spinner inside the menu.

false | true

maxH

Whether to restrict the max-height of the content.

Content is also restricted by the available height in the screen relative to the trigger.

"xs" | "sm" | "md" | "lg" | "full"

minW

Whether to set the min-width to the width of the trigger.

"0" | "trigger"

onCloseAutoFocus

Event handler called when auto-focusing on close. Can be prevented.

(event: Event) => void

onEscapeKeyDown

(event: KeyboardEvent) => void

onFocusOutside

(event: FocusOutsideEvent) => void

onInteractOutside

(event: FocusOutsideEvent | PointerDownOutsideEvent) => void

onPointerDownOutside

(event: PointerDownOutsideEvent) => void

side

"bottom" | "left" | "right" | "top"

DropdownMenuItem

Supports all Box props in addition to its own. Renders a <div> element.

Prop

addonAfter

Display content inside the item after children.

ReactNode

addonBefore

Display content inside the item before children.

ReactNode

asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

false | true

className

string

description

Add secondary text after the primary label.

ReactNode

disabled

false | true

icon

Display an icon before the item content.

ReactNode

intent

Control the appearance by selecting between the different item types.

"danger" | "neutral"

onSelect

(event: Event) => void

textValue

string

DropdownMenuLabel

Supports all Box props in addition to its own. Renders a <div> element.

Prop

asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

false | true

className

string

DropdownMenuGroup

Supports all Box props in addition to its own. Renders a <div> element.

Prop

asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

false | true

className

string

DropdownMenuSeparator

Supports all Separator props in addition to its own. Renders a <div> element.

Prop

asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

false | true

className

string

DropdownMenuSub

Doesn't render its own HTML element.

Prop

defaultOpen

The initial open state in uncontrolled mode.

false | true

onOpenChange

Handler that is called when the open state changes.

(open: boolean) => void

open

The open state in controlled mode.

false | true

DropdownMenuSubTrigger

Supports all Box props in addition to its own. Renders a <div> element.

Prop

addonAfter

Display content inside the item after children.

ReactNode

addonBefore

Display content inside the item before children.

ReactNode

asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

false | true

className

string

description

Add secondary text after the primary label.

ReactNode

disabled

false | true

icon

Display an icon before the item content.

ReactNode

intent

Control the appearance by selecting between the different item types.

"danger" | "neutral"

textValue

string

DropdownMenuSubContent

Supports all Box props in addition to its own. Renders a <div> element.

Prop

asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

false | true

className

string

maxH

Whether to restrict the max-height of the content.

Content is also restricted by the available height in the screen relative to the trigger.

"xs" | "sm" | "md" | "lg" | "full"

minW

Whether to set the min-width to the width of the trigger.

"0" | "trigger"

onEscapeKeyDown

(event: KeyboardEvent) => void

onFocusOutside

(event: FocusOutsideEvent) => void

onInteractOutside

(event: FocusOutsideEvent | PointerDownOutsideEvent) => void

onPointerDownOutside

(event: PointerDownOutsideEvent) => void

Changelog

0.1.0

  • Added component