2023-06-06 12:22:18 +00:00
---
meta:
title: Dropdown
description: 'Dropdowns expose additional content that "drops down" in a panel.'
layout: component
---
Dropdowns consist of a trigger and a panel. By default, activating the trigger will expose the panel and interacting outside of the panel will close it.
2023-07-27 16:39:44 +00:00
Dropdowns are designed to work well with [menus ](/components/menu ) to provide a list of options the user can select from. However, dropdowns can also be used in lower-level applications (e.g. [color picker ](/components/color-picker )). The API gives you complete control over showing, hiding, and positioning the panel.
2023-06-06 12:22:18 +00:00
```html:preview
< sl-dropdown >
< sl-button slot = "trigger" caret > Dropdown< / sl-button >
< sl-menu >
< sl-menu-item > Dropdown Item 1< / sl-menu-item >
< sl-menu-item > Dropdown Item 2< / sl-menu-item >
< sl-menu-item > Dropdown Item 3< / sl-menu-item >
< sl-divider > < / sl-divider >
< sl-menu-item type = "checkbox" checked > Checkbox< / sl-menu-item >
< sl-menu-item disabled > Disabled< / sl-menu-item >
< sl-divider > < / sl-divider >
< sl-menu-item >
Prefix
< sl-icon slot = "prefix" name = "gift" > < / sl-icon >
< / sl-menu-item >
< sl-menu-item >
Suffix Icon
< sl-icon slot = "suffix" name = "heart" > < / sl-icon >
< / sl-menu-item >
< / sl-menu >
< / sl-dropdown >
```
```jsx:react
2023-08-11 17:09:44 +00:00
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
import SlDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
2023-06-06 12:22:18 +00:00
const App = () => (
< SlDropdown >
< SlButton slot = "trigger" caret >
Dropdown
< / SlButton >
< SlMenu >
< SlMenuItem > Dropdown Item 1< / SlMenuItem >
< SlMenuItem > Dropdown Item 2< / SlMenuItem >
< SlMenuItem > Dropdown Item 3< / SlMenuItem >
< SlDivider / >
< SlMenuItem type = "checkbox" checked >
Checkbox
< / SlMenuItem >
< SlMenuItem disabled > Disabled< / SlMenuItem >
< SlDivider / >
< SlMenuItem >
Prefix
< SlIcon slot = "prefix" name = "gift" / >
< / SlMenuItem >
< SlMenuItem >
Suffix Icon
< SlIcon slot = "suffix" name = "heart" / >
< / SlMenuItem >
< / SlMenu >
< / SlDropdown >
);
```
## Examples
### Getting the Selected Item
When dropdowns are used with [menus ](/components/menu ), you can listen for the [`sl-select` ](/components/menu#events ) event to determine which menu item was selected. The menu item element will be exposed in `event.detail.item` . You can set `value` props to make it easier to identify commands.
```html:preview
< div class = "dropdown-selection" >
< sl-dropdown >
< sl-button slot = "trigger" caret > Edit< / sl-button >
< sl-menu >
< sl-menu-item value = "cut" > Cut< / sl-menu-item >
< sl-menu-item value = "copy" > Copy< / sl-menu-item >
< sl-menu-item value = "paste" > Paste< / sl-menu-item >
< / sl-menu >
< / sl-dropdown >
< / div >
< script >
const container = document.querySelector('.dropdown-selection');
const dropdown = container.querySelector('sl-dropdown');
dropdown.addEventListener('sl-select', event => {
const selectedItem = event.detail.item;
console.log(selectedItem.value);
});
< / script >
```
```jsx:react
2023-08-11 17:09:44 +00:00
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
2023-06-06 12:22:18 +00:00
const App = () => {
function handleSelect(event) {
const selectedItem = event.detail.item;
console.log(selectedItem.value);
}
return (
< SlDropdown >
< SlButton slot = "trigger" caret >
Edit
< / SlButton >
< SlMenu onSlSelect = {handleSelect} >
< SlMenuItem value = "cut" > Cut< / SlMenuItem >
< SlMenuItem value = "copy" > Copy< / SlMenuItem >
< SlMenuItem value = "paste" > Paste< / SlMenuItem >
< / SlMenu >
< / SlDropdown >
);
};
```
Alternatively, you can listen for the `click` event on individual menu items. Note that, using this approach, disabled menu items will still emit a `click` event.
```html:preview
< div class = "dropdown-selection-alt" >
< sl-dropdown >
< sl-button slot = "trigger" caret > Edit< / sl-button >
< sl-menu >
< sl-menu-item value = "cut" > Cut< / sl-menu-item >
< sl-menu-item value = "copy" > Copy< / sl-menu-item >
< sl-menu-item value = "paste" > Paste< / sl-menu-item >
< / sl-menu >
< / sl-dropdown >
< / div >
< script >
const container = document.querySelector('.dropdown-selection-alt');
const cut = container.querySelector('sl-menu-item[value="cut"]');
const copy = container.querySelector('sl-menu-item[value="copy"]');
const paste = container.querySelector('sl-menu-item[value="paste"]');
cut.addEventListener('click', () => console.log('cut'));
copy.addEventListener('click', () => console.log('copy'));
paste.addEventListener('click', () => console.log('paste'));
< / script >
```
```jsx:react
2023-08-11 17:09:44 +00:00
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
2023-06-06 12:22:18 +00:00
const App = () => {
function handleCut() {
console.log('cut');
}
function handleCopy() {
console.log('copy');
}
function handlePaste() {
console.log('paste');
}
return (
< SlDropdown >
< SlButton slot = "trigger" caret >
Edit
< / SlButton >
< SlMenu >
< SlMenuItem onClick = {handleCut} > Cut< / SlMenuItem >
< SlMenuItem onClick = {handleCopy} > Copy< / SlMenuItem >
< SlMenuItem onClick = {handlePaste} > Paste< / SlMenuItem >
< / SlMenu >
< / SlDropdown >
);
};
```
### Placement
The preferred placement of the dropdown can be set with the `placement` attribute. Note that the actual position may vary to ensure the panel remains in the viewport.
```html:preview
< sl-dropdown placement = "top-start" >
< sl-button slot = "trigger" caret > Edit< / sl-button >
< sl-menu >
< sl-menu-item > Cut< / sl-menu-item >
< sl-menu-item > Copy< / sl-menu-item >
< sl-menu-item > Paste< / sl-menu-item >
< sl-divider > < / sl-divider >
< sl-menu-item > Find< / sl-menu-item >
< sl-menu-item > Replace< / sl-menu-item >
< / sl-menu >
< / sl-dropdown >
```
```jsx:react
2023-08-11 17:09:44 +00:00
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
import SlDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
2023-06-06 12:22:18 +00:00
const App = () => (
< SlDropdown placement = "top-start" >
< SlButton slot = "trigger" caret >
Edit
< / SlButton >
< SlMenu >
< SlMenuItem > Cut< / SlMenuItem >
< SlMenuItem > Copy< / SlMenuItem >
< SlMenuItem > Paste< / SlMenuItem >
< SlDivider / >
< SlMenuItem > Find< / SlMenuItem >
< SlMenuItem > Replace< / SlMenuItem >
< / SlMenu >
< / SlDropdown >
);
```
### Distance
The distance from the panel to the trigger can be customized using the `distance` attribute. This value is specified in pixels.
```html:preview
< sl-dropdown distance = "30" >
< sl-button slot = "trigger" caret > Edit< / sl-button >
< sl-menu >
< sl-menu-item > Cut< / sl-menu-item >
< sl-menu-item > Copy< / sl-menu-item >
< sl-menu-item > Paste< / sl-menu-item >
< sl-divider > < / sl-divider >
< sl-menu-item > Find< / sl-menu-item >
< sl-menu-item > Replace< / sl-menu-item >
< / sl-menu >
< / sl-dropdown >
```
```jsx:react
2023-08-11 17:09:44 +00:00
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
import SlDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
2023-06-06 12:22:18 +00:00
const App = () => (
< SlDropdown distance = {30} >
< SlButton slot = "trigger" caret >
Edit
< / SlButton >
< SlMenu >
< SlMenuItem > Cut< / SlMenuItem >
< SlMenuItem > Copy< / SlMenuItem >
< SlMenuItem > Paste< / SlMenuItem >
< SlDivider / >
< SlMenuItem > Find< / SlMenuItem >
< SlMenuItem > Replace< / SlMenuItem >
< / SlMenu >
< / SlDropdown >
);
```
### Skidding
The offset of the panel along the trigger can be customized using the `skidding` attribute. This value is specified in pixels.
```html:preview
< sl-dropdown skidding = "30" >
< sl-button slot = "trigger" caret > Edit< / sl-button >
< sl-menu >
< sl-menu-item > Cut< / sl-menu-item >
< sl-menu-item > Copy< / sl-menu-item >
< sl-menu-item > Paste< / sl-menu-item >
< sl-divider > < / sl-divider >
< sl-menu-item > Find< / sl-menu-item >
< sl-menu-item > Replace< / sl-menu-item >
< / sl-menu >
< / sl-dropdown >
```
```jsx:react
2023-08-11 17:09:44 +00:00
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
import SlDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
2023-06-06 12:22:18 +00:00
const App = () => (
< SlDropdown skidding = {30} >
< SlButton slot = "trigger" caret >
Edit
< / SlButton >
< SlMenu >
< SlMenuItem > Cut< / SlMenuItem >
< SlMenuItem > Copy< / SlMenuItem >
< SlMenuItem > Paste< / SlMenuItem >
< SlDivider / >
< SlMenuItem > Find< / SlMenuItem >
< SlMenuItem > Replace< / SlMenuItem >
< / SlMenu >
< / SlDropdown >
);
```
2023-08-21 21:26:41 +00:00
### Submenus
To create a submenu, nest an `<sl-menu slot="submenu">` element in a [menu item ](/components/menu-item ).
```html:preview
< sl-dropdown >
< sl-button slot = "trigger" caret > Edit< / sl-button >
< sl-menu style = "max-width: 200px;" >
< sl-menu-item value = "undo" > Undo< / sl-menu-item >
< sl-menu-item value = "redo" > Redo< / sl-menu-item >
< sl-divider > < / sl-divider >
< sl-menu-item value = "cut" > Cut< / sl-menu-item >
< sl-menu-item value = "copy" > Copy< / sl-menu-item >
< sl-menu-item value = "paste" > Paste< / sl-menu-item >
< sl-divider > < / sl-divider >
< sl-menu-item >
Find
< sl-menu slot = "submenu" >
< sl-menu-item value = "find" > Find…< / sl-menu-item >
< sl-menu-item value = "find-previous" > Find Next< / sl-menu-item >
< sl-menu-item value = "find-next" > Find Previous< / sl-menu-item >
< / sl-menu >
< / sl-menu-item >
< sl-menu-item >
Transformations
< sl-menu slot = "submenu" >
< sl-menu-item value = "uppercase" > Make uppercase< / sl-menu-item >
< sl-menu-item value = "lowercase" > Make lowercase< / sl-menu-item >
< sl-menu-item value = "capitalize" > Capitalize< / sl-menu-item >
< / sl-menu >
< / sl-menu-item >
< / sl-menu >
< / sl-dropdown >
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
import SlDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
const css = `
.dropdown-hoist {
border: solid 2px var(--sl-panel-border-color);
padding: var(--sl-spacing-medium);
overflow: hidden;
}
`;
const App = () => (
< >
< SlDropdown >
< SlButton slot = "trigger" caret > Edit< / SlButton >
< SlMenu style = "max-width: 200px;" >
< SlMenuItem value = "undo" > Undo< / SlMenuItem >
< SlMenuItem value = "redo" > Redo< / SlMenuItem >
< SlDivider / >
< SlMenuItem value = "cut" > Cut< / SlMenuItem >
< SlMenuItem value = "copy" > Copy< / SlMenuItem >
< SlMenuItem value = "paste" > Paste< / SlMenuItem >
< SlDivider / >
< SlMenuItem >
Find
< SlMenu slot = "submenu" >
< SlMenuItem value = "find" > Find…< / SlMenuItem >
< SlMenuItem value = "find-previous" > Find Next< / SlMenuItem >
< SlMenuItem value = "find-next" > Find Previous< / SlMenuItem >
< / SlMenu >
< / SlMenuItem >
< SlMenuItem >
Transformations
< SlMenu slot = "submenu" >
< SlMenuItem value = "uppercase" > Make uppercase< / SlMenuItem >
< SlMenuItem value = "lowercase" > Make lowercase< / SlMenuItem >
< SlMenuItem value = "capitalize" > Capitalize< / SlMenuItem >
< / SlMenu >
< / SlMenuItem >
< / SlMenu >
< / SlDropdown >
< />
);
```
:::warning
As a UX best practice, avoid using more than one level of submenu when possible.
:::
2023-06-06 12:22:18 +00:00
### Hoisting
2023-07-03 19:50:58 +00:00
Dropdown panels will be clipped if they're inside a container that has `overflow: auto|hidden` . The `hoist` attribute forces the panel to use a fixed positioning strategy, allowing it to break out of the container. In this case, the panel will be positioned relative to its [containing block ](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#Identifying_the_containing_block ), which is usually the viewport unless an ancestor uses a `transform` , `perspective` , or `filter` . [Refer to this page ](https://developer.mozilla.org/en-US/docs/Web/CSS/position#fixed ) for more details.
2023-06-06 12:22:18 +00:00
```html:preview
< div class = "dropdown-hoist" >
< sl-dropdown >
< sl-button slot = "trigger" caret > No Hoist< / sl-button >
< sl-menu >
< sl-menu-item > Item 1< / sl-menu-item >
< sl-menu-item > Item 2< / sl-menu-item >
< sl-menu-item > Item 3< / sl-menu-item >
< / sl-menu >
< / sl-dropdown >
< sl-dropdown hoist >
< sl-button slot = "trigger" caret > Hoist< / sl-button >
< sl-menu >
< sl-menu-item > Item 1< / sl-menu-item >
< sl-menu-item > Item 2< / sl-menu-item >
< sl-menu-item > Item 3< / sl-menu-item >
< / sl-menu >
< / sl-dropdown >
< / div >
< style >
.dropdown-hoist {
position: relative;
border: solid 2px var(--sl-panel-border-color);
padding: var(--sl-spacing-medium);
overflow: hidden;
}
< / style >
```
```jsx:react
2023-08-11 17:09:44 +00:00
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
import SlDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
2023-06-06 12:22:18 +00:00
const css = `
.dropdown-hoist {
border: solid 2px var(--sl-panel-border-color);
padding: var(--sl-spacing-medium);
overflow: hidden;
}
`;
const App = () => (
< >
< div className = "dropdown-hoist" >
< SlDropdown >
< SlButton slot = "trigger" caret >
No Hoist
< / SlButton >
< SlMenu >
< SlMenuItem > Item 1< / SlMenuItem >
< SlMenuItem > Item 2< / SlMenuItem >
< SlMenuItem > Item 3< / SlMenuItem >
< / SlMenu >
< / SlDropdown >
< SlDropdown hoist >
< SlButton slot = "trigger" caret >
Hoist
< / SlButton >
< SlMenu >
< SlMenuItem > Item 1< / SlMenuItem >
< SlMenuItem > Item 2< / SlMenuItem >
< SlMenuItem > Item 3< / SlMenuItem >
< / SlMenu >
< / SlDropdown >
< / div >
< style > { c s s } < / style >
< />
);
```