Components
- Accordion
- Alert
- Alert Dialog
- Autocomplete
- Avatar
- Badge
- Breadcrumb
- Button
- Card
- Checkbox
- Checkbox Group
- Collapsible
- Combobox
- Dialog
- Empty
- Field
- Fieldset
- Form
- Frame
- Group
- Input
- Label
- Menu
- Meter
- Number Field
- Pagination
- Popover
- Preview Card
- Progress
- Radio Group
- Scroll Area
- Select
- Separator
- Sheet
- Skeleton
- Slider
- Switch
- Table
- Tabs
- Textarea
- Toast
- Toggle
- Toggle Group
- Toolbar
- Tooltip
Menu
A list of actions in a dropdown, enhanced with keyboard navigation.
import {
PauseIcon,
PlayIcon,
SkipBackIcon,
SkipForwardIcon,
TrashIcon,
} from "lucide-react"
import { Button } from "@/components/ui/button"
import {
Menu,
MenuCheckboxItem,
MenuGroup,
MenuGroupLabel,
MenuItem,
MenuPopup,
MenuRadioGroup,
MenuRadioItem,
MenuSeparator,
MenuShortcut,
MenuSub,
MenuSubPopup,
MenuSubTrigger,
MenuTrigger,
} from "@/components/ui/menu"
export default function MenuDemo() {
return (
<Menu>
<MenuTrigger render={<Button variant="outline" />}>Open menu</MenuTrigger>
<MenuPopup>
<MenuGroup>
<MenuGroupLabel>Playback</MenuGroupLabel>
<MenuItem>
<PlayIcon className="opacity-72" />
Play
<MenuShortcut>⌘P</MenuShortcut>
</MenuItem>
<MenuItem disabled>
<PauseIcon className="opacity-72" />
Pause
<MenuShortcut>⇧⌘P</MenuShortcut>
</MenuItem>
<MenuItem>
<SkipBackIcon className="opacity-72" />
Previous
<MenuShortcut>⌘[</MenuShortcut>
</MenuItem>
<MenuItem>
<SkipForwardIcon className="opacity-72" />
Next
<MenuShortcut>⌘]</MenuShortcut>
</MenuItem>
</MenuGroup>
<MenuSeparator />
<MenuCheckboxItem>Shuffle</MenuCheckboxItem>
<MenuCheckboxItem>Repeat</MenuCheckboxItem>
<MenuCheckboxItem disabled>Enhanced Audio</MenuCheckboxItem>
<MenuSeparator />
<MenuGroup>
<MenuGroupLabel>Sort by</MenuGroupLabel>
<MenuRadioGroup>
<MenuRadioItem value="artist">Artist</MenuRadioItem>
<MenuRadioItem value="album">Album</MenuRadioItem>
<MenuRadioItem value="title">Title</MenuRadioItem>
</MenuRadioGroup>
</MenuGroup>
<MenuSeparator />
<MenuSub>
<MenuSubTrigger>Add to Playlist</MenuSubTrigger>
<MenuSubPopup>
<MenuItem>Jazz</MenuItem>
<MenuSub>
<MenuSubTrigger>Rock</MenuSubTrigger>
<MenuSubPopup>
<MenuItem>Hard Rock</MenuItem>
<MenuItem>Soft Rock</MenuItem>
<MenuItem>Classic Rock</MenuItem>
<MenuSeparator />
<MenuItem>Metal</MenuItem>
<MenuItem>Punk</MenuItem>
<MenuItem>Grunge</MenuItem>
<MenuItem>Alternative</MenuItem>
<MenuItem>Indie</MenuItem>
<MenuItem>Electronic</MenuItem>
</MenuSubPopup>
</MenuSub>
<MenuItem>Pop</MenuItem>
</MenuSubPopup>
</MenuSub>
<MenuSeparator />
<MenuItem variant="destructive">
<TrashIcon />
Delete
<MenuShortcut>⌘⌫</MenuShortcut>
</MenuItem>
</MenuPopup>
</Menu>
)
}
Installation
npx love-ui@latest add menuUsage
import {
Menu,
MenuCheckboxItem,
MenuGroup,
MenuGroupLabel,
MenuItem,
MenuPopup,
MenuRadioGroup,
MenuRadioItem,
MenuSeparator,
MenuSub,
MenuSubPopup,
MenuSubTrigger,
MenuTrigger,
} from "@/components/ui/menu"<Menu>
<MenuTrigger>Open</MenuTrigger>
<MenuPopup align="start" sideOffset={4}>
<MenuItem>Profile</MenuItem>
<MenuSeparator />
<MenuGroup>
<MenuGroupLabel>Playback</MenuGroupLabel>
<MenuItem>Play</MenuItem>
<MenuItem>Pause</MenuItem>
</MenuGroup>
<MenuSeparator />
<MenuCheckboxItem>Shuffle</MenuCheckboxItem>
<MenuCheckboxItem>Repeat</MenuCheckboxItem>
<MenuSeparator />
<MenuGroup>
<MenuGroupLabel>Sort by</MenuGroupLabel>
<MenuRadioGroup>
<MenuRadioItem>Artist</MenuRadioItem>
<MenuRadioItem>Album</MenuRadioItem>
<MenuRadioItem>Title</MenuRadioItem>
</MenuRadioGroup>
</MenuGroup>
<MenuSeparator />
<MenuSub>
<MenuSubTrigger>Add to playlist</MenuSubTrigger>
<MenuSubPopup>
<MenuItem>Jazz</MenuItem>
<MenuItem>Rock</MenuItem>
</MenuSubPopup>
</MenuSub>
</MenuPopup>
</Menu>Examples
Open on Hover
import { Button } from "@/components/ui/button"
import {
Menu,
MenuItem,
MenuPopup,
MenuTrigger,
} from "@/components/ui/menu"
export default function MenuHoverDemo() {
return (
<Menu openOnHover>
<MenuTrigger render={<Button variant="outline" />}>Hover me</MenuTrigger>
<MenuPopup>
<MenuItem>Item one</MenuItem>
<MenuItem>Item two</MenuItem>
</MenuPopup>
</Menu>
)
}
With Checkbox
import { Button } from "@/components/ui/button"
import {
Menu,
MenuCheckboxItem,
MenuPopup,
MenuTrigger,
} from "@/components/ui/menu"
export default function MenuCheckboxDemo() {
return (
<Menu>
<MenuTrigger render={<Button variant="outline" />}>Open menu</MenuTrigger>
<MenuPopup>
<MenuCheckboxItem defaultChecked>Auto save</MenuCheckboxItem>
<MenuCheckboxItem>Notifications</MenuCheckboxItem>
</MenuPopup>
</Menu>
)
}
With Radio Group
import { Button } from "@/components/ui/button"
import {
Menu,
MenuPopup,
MenuRadioGroup,
MenuRadioItem,
MenuTrigger,
} from "@/components/ui/menu"
export default function MenuRadioGroupDemo() {
return (
<Menu>
<MenuTrigger render={<Button variant="outline" />}>Open menu</MenuTrigger>
<MenuPopup>
<MenuRadioGroup defaultValue="system">
<MenuRadioItem value="light">Light</MenuRadioItem>
<MenuRadioItem value="dark">Dark</MenuRadioItem>
<MenuRadioItem value="system">System</MenuRadioItem>
</MenuRadioGroup>
</MenuPopup>
</Menu>
)
}
With Link
import Link from "next/link"
import { Button } from "@/components/ui/button"
import {
Menu,
MenuItem,
MenuPopup,
MenuTrigger,
} from "@/components/ui/menu"
export default function MenuLinkDemo() {
return (
<Menu>
<MenuTrigger render={<Button variant="outline" />}>Open menu</MenuTrigger>
<MenuPopup>
<MenuItem render={<Link href="/docs" />}>Docs</MenuItem>
<MenuItem render={<Link href="/particles" />}>Particles</MenuItem>
</MenuPopup>
</Menu>
)
}
With Group Label
import { Button } from "@/components/ui/button"
import {
Menu,
MenuGroup,
MenuGroupLabel,
MenuItem,
MenuPopup,
MenuSeparator,
MenuTrigger,
} from "@/components/ui/menu"
export default function MenuGroupLabelsDemo() {
return (
<Menu>
<MenuTrigger render={<Button variant="outline" />}>Open menu</MenuTrigger>
<MenuPopup>
<MenuGroup>
<MenuGroupLabel>Account</MenuGroupLabel>
<MenuItem>Profile</MenuItem>
<MenuItem>Billing</MenuItem>
</MenuGroup>
<MenuSeparator />
<MenuGroup>
<MenuGroupLabel>Support</MenuGroupLabel>
<MenuItem>Docs</MenuItem>
<MenuItem>Contact</MenuItem>
</MenuGroup>
</MenuPopup>
</Menu>
)
}
Nested Menu
import { Button } from "@/components/ui/button"
import {
Menu,
MenuItem,
MenuPopup,
MenuSub,
MenuSubPopup,
MenuSubTrigger,
MenuTrigger,
} from "@/components/ui/menu"
export default function MenuNestedDemo() {
return (
<Menu>
<MenuTrigger render={<Button variant="outline" />}>Open menu</MenuTrigger>
<MenuPopup>
<MenuItem>Item one</MenuItem>
<MenuSub>
<MenuSubTrigger>More</MenuSubTrigger>
<MenuSubPopup>
<MenuItem>Sub item A</MenuItem>
<MenuItem>Sub item B</MenuItem>
</MenuSubPopup>
</MenuSub>
<MenuItem>Item two</MenuItem>
</MenuPopup>
</Menu>
)
}
Close on Click
import { Button } from "@/components/ui/button"
import {
Menu,
MenuItem,
MenuPopup,
MenuTrigger,
} from "@/components/ui/menu"
export default function MenuCloseOnClickDemo() {
return (
<Menu>
<MenuTrigger render={<Button variant="outline" />}>Open menu</MenuTrigger>
<MenuPopup>
<MenuItem closeOnClick>Profile</MenuItem>
<MenuItem closeOnClick>Settings</MenuItem>
<MenuItem closeOnClick>Log out</MenuItem>
</MenuPopup>
</Menu>
)
}
Open a Dialog
"use client"
import * as React from "react"
import { Button } from "@/components/ui/button"
import {
Dialog,
DialogClose,
DialogDescription,
DialogFooter,
DialogHeader,
DialogPopup,
DialogTitle,
} from "@/components/ui/dialog"
import {
Menu,
MenuItem,
MenuPopup,
MenuTrigger,
} from "@/components/ui/menu"
export default function DialogFromMenuDemo() {
const [dialogOpen, setDialogOpen] = React.useState(false)
return (
<>
<Menu>
<MenuTrigger render={<Button variant="outline" />}>
Open menu
</MenuTrigger>
<MenuPopup align="start">
<MenuItem onClick={() => setDialogOpen(true)}>Open dialog</MenuItem>
</MenuPopup>
</Menu>
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
<DialogPopup>
<DialogHeader>
<DialogTitle>Settings</DialogTitle>
<DialogDescription>Change your preferences</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose render={<Button variant="ghost" />}>Close</DialogClose>
</DialogFooter>
</DialogPopup>
</Dialog>
</>
)
}
Comparing with Radix / shadcn
If you’re already familiar with Radix UI and shadcn/ui, this guide highlights the small differences and similarities so you can get started with loveui quickly.
Prop Mapping
| Component | Radix UI Prop | Base UI Prop |
|---|---|---|
MenuItem | onSelect | onClick |
Quick Checklist
- If you used
asChildon parts, switch to therenderprop - Replace
onSelect→onClickon menu items - Update import path from
@/components/ui/dropdown-menu→@/components/ui/menu - Prefer
Menu*component names;DropdownMenu*remain for legacy - Prefer
MenuGroupLabelinstead ofDropdownMenuLabel - Prefer
MenuPopupinstead ofDropdownMenuContent - Prefer
MenuSubPopupinstead ofDropdownMenuSubContent
Comparison Examples
<DropdownMenu>
<DropdownMenuTrigger>Open menu</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem
onSelect={() => {
console.log("Dashboard")
}}
>
Dashboard
</DropdownMenuItem>
<DropdownMenuItem>Settings</DropdownMenuItem>
<DropdownMenuItem>Sign out</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu><Menu>
<MenuTrigger>Open menu</MenuTrigger>
<MenuPopup>
<MenuItem
onClick={() => {
console.log("Dashboard")
}}
>
Dashboard
</MenuItem>
<MenuItem>Settings</MenuItem>
<MenuItem>Sign out</MenuItem>
</MenuPopup>
</Menu>