Dropdown
Dropdowns are toggleable, contextual overlays for displaying lists of links and actions in a dropdown menu format.
Simple Dropdown
import { useState } from 'react'
import { ChevronDown } from 'lucide-react'
export default function Component() {
const [isOpen, setIsOpen] = useState(false)
const [selected, setSelected] = useState('Choose an option')
const options = ['Option 1', 'Option 2', 'Option 3']
return (
<div className="relative inline-block text-left">
<div>
<button
type="button"
className="inline-flex justify-center w-full rounded-md border border-gray-300 dark:border-gray-600 shadow-sm px-4 py-2 bg-white dark:bg-gray-800 text-sm font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700"
id="options-menu"
aria-haspopup="true"
aria-expanded="true"
onClick={() => setIsOpen(!isOpen)}
>
{selected}
<ChevronDown className="-mr-1 ml-2 h-5 w-5" aria-hidden="true" />
</button>
</div>
{isOpen && (
<div className="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white dark:bg-gray-800">
<div className="py-1" role="menu" aria-orientation="vertical" aria-labelledby="options-menu">
{options.map((option) => (
<a
key={option}
href="#"
className="block px-4 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-white"
role="menuitem"
onClick={() => {
setSelected(option)
setIsOpen(false)
}}
>
{option}
</a>
))}
</div>
</div>
)}
</div>
)
}
Dropdown with Icons
import { useState } from 'react'
import { ChevronDown, Mail, Settings, LogOut } from 'lucide-react'
export default function Component() {
const [isOpen, setIsOpen] = useState(false)
const options = [
{ label: 'Messages', icon: Mail },
{ label: 'Settings', icon: Settings },
{ label: 'Logout', icon: LogOut },
]
return (
<div className="relative inline-block text-left">
<div>
<button
type="button"
className="inline-flex justify-center w-full rounded-md border border-gray-300 dark:border-gray-600 shadow-sm px-4 py-2 bg-white dark:bg-gray-800 text-sm font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700"
id="options-menu"
aria-haspopup="true"
aria-expanded="true"
onClick={() => setIsOpen(!isOpen)}
>
Options
<ChevronDown className="-mr-1 ml-2 h-5 w-5" aria-hidden="true" />
</button>
</div>
{isOpen && (
<div className="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white dark:bg-gray-800">
<div className="py-1" role="menu" aria-orientation="vertical" aria-labelledby="options-menu">
{options.map((option) => (
<a
key={option.label}
href="#"
className="flex items-center px-4 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-white"
role="menuitem"
onClick={(e) => {
e.preventDefault()
setIsOpen(false)
// Handle option selection here
}}
>
<option.icon className="mr-3 h-5 w-5 text-gray-400 dark:text-gray-300" aria-hidden="true" />
{option.label}
</a>
))}
</div>
</div>
)}
</div>
)
}
Dropdown with Search
import { useState, useEffect, useRef } from 'react'
import { Search, ChevronDown } from 'lucide-react'
export default function Component() {
const [isOpen, setIsOpen] = useState(false)
const [searchTerm, setSearchTerm] = useState('')
const [selected, setSelected] = useState(null)
const dropdownRef = useRef(null)
const options = [
'Apple', 'Banana', 'Cherry', 'Date', 'Elderberry',
'Fig', 'Grape', 'Honeydew', 'Kiwi', 'Lemon'
]
const filteredOptions = options.filter(option =>
option.toLowerCase().includes(searchTerm.toLowerCase())
)
useEffect(() => {
const handleClickOutside = (event) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
setIsOpen(false)
}
}
document.addEventListener('mousedown', handleClickOutside)
return () => {
document.removeEventListener('mousedown', handleClickOutside)
}
}, [])
return (
<div className="relative inline-block text-left" ref={dropdownRef}>
<div>
<button
type="button"
className="inline-flex justify-between w-64 rounded-md border border-gray-300 dark:border-gray-600 shadow-sm px-4 py-2 bg-white dark:bg-gray-800 text-sm font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700"
id="options-menu"
aria-haspopup="true"
aria-expanded="true"
onClick={() => setIsOpen(!isOpen)}
>
{selected || 'Select a fruit'}
<ChevronDown className="-mr-1 ml-2 h-5 w-5" aria-hidden="true" />
</button>
</div>
{isOpen && (
<div className="ororigin-top-right absolute right-0 mt-2 w-64 rounded-md shadow-lg bg-white dark:bg-gray-800">
<div className="p-2">
<div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 dark:text-gray-300" />
<input
type="text"
className="w-full pl-10 pr-4 py-2 border rounded-md text-sm dark:bg-gray-700 dark:border-gray-500 dark:text-gray-200"
placeholder="Search fruits..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</div>
</div>
<div className="max-h-60 overflow-y-auto">
{filteredOptions.map((option) => (
<a
key={option}
href="#"
className="block px-4 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-white"
onClick={(e) => {
e.preventDefault()
setSelected(option)
setIsOpen(false)
}}
>
{option}
</a>
))}
</div>
</div>
)}
</div>
)
}