@preline/dropdown
v4.2.0
Published
Preline UI is an open-source set of prebuilt UI components based on the utility-first Tailwind CSS framework.
Downloads
26,238
Readme
Dropdown
A dropdown menu displays a list of actions and more with JavaScript dropdown plugin.
Contents
- Overview
- Installation
- Basic usage
- Accessibility notes
- Configuration Options
- JavaScript API
- Events
- Common Patterns
- License
Overview
The Dropdown component provides a menu that appears when triggered, displaying a list of actions or options. It supports multiple placement options, customizable triggers, keyboard navigation, and integrates with Floating UI for advanced positioning.
Key Features:
- Multiple placement options (top, bottom, left, right, auto)
- Customizable trigger events (click, hover, contextmenu)
- Floating UI integration for smart positioning
- Keyboard navigation support (Arrow keys, Home, End, Esc, A-Z)
- Programmatic control via JavaScript API
- Event system for lifecycle hooks
- Accessibility attributes (ARIA) built-in
Installation
To get started, install Dropdown plugin via npm, else you can skip this step if you are already using Preline UI as a package.
npm i @preline/dropdownCSS
Use @source to register the plugin's JavaScript path for Tailwind CSS scanning, then @import the plugin's CSS files into your Tailwind CSS file.
@import "tailwindcss";
/* @preline/dropdown */
@source "../node_modules/@preline/dropdown/*.js";
@import "./node_modules/@preline/dropdown/variants.css";
@import "./node_modules/@preline/dropdown/theme.css";JavaScript
Include the JavaScript that powers the interactive elements near the end of your </body> tag:
<script src="./node_modules/@preline/dropdown/index.js"></script>Manual Initialization
Use the non-auto entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.
<script type="module">
import HSDropdown from "@preline/dropdown/non-auto.mjs";
new HSDropdown(document.querySelector("#dropdown"));
</script>Via Bundler
When using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.
@preline/dropdown is the auto-init entry: it scans the DOM and initializes matching elements automatically.
import "@preline/dropdown";@preline/dropdown/non-auto is the manual entry: use it when you want explicit control over when initialization happens, either via autoInit() or by creating a specific instance yourself.
import HSDropdown from "@preline/dropdown/non-auto";
HSDropdown.autoInit();
// Or initialize a specific element manually
const el = document.querySelector("#dropdown");
if (el) new HSDropdown(el);TypeScript
This package ships with TypeScript type definitions. No additional @types/ package is needed.
Basic usage
The following example demonstrates the minimal HTML structure required for a dropdown component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The dropdown menu appears when clicking the button.
<div class="hs-dropdown relative inline-flex">
<button id="hs-dropdown-unstyled" type="button" class="hs-dropdown-toggle inline-flex justify-center items-center gap-x-2" aria-expanded="false" aria-label="Menu">
Actions
</button>
<div class="hs-dropdown-menu transition-[opacity,margin] duration hs-dropdown-open:opacity-100 opacity-0 w-56 hidden z-10 mt-2 min-w-60 bg-white" role="menu" aria-labelledby="hs-dropdown-unstyled">
<a class="block" href="#">Newsletter</a>
<a class="block" href="#">Purchases</a>
<a class="block" href="#">Downloads</a>
<a class="block" href="#">Team Account</a>
</div>
</div>Structure Requirements:
hs-dropdown: Required container wrapperhs-dropdown-toggle: Required button element that triggers the dropdownhs-dropdown-menu: Required container for dropdown menu items- Proper ARIA attributes (
aria-expanded,aria-label,role="menu",aria-labelledby)
Initial State:
- Set
aria-expanded="false"on the toggle button initially - Add
hiddenclass to the dropdown menu initially - Menu visibility is controlled by
hs-dropdown-open:*modifier classes
Accessibility notes
Keyboard interactions
| Command | Description | | --- | --- | | Enter | Activates the selected menu item. | | ArrowUp / ArrowDown | Focuses previous/next non-disabled item. | | Home / End | Focuses first/last non-disabled item. | | Esc | Closes any open dropdown menus. | | A-Z / a-z | Focuses first item that matches keyboard input. |
Configuration Options
Data Attributes
Data attributes are added directly to HTML elements to configure dropdown behavior.
| Attribute | Target Element | Type | Default | Description |
| --- | --- | --- | --- | --- |
| data-hs-dropdown-transition | hs-dropdown (container) | - | - | Data attribute to designate the container to be animated. When present, enables transition animations for the dropdown menu. |
CSS Classes (Modifiers)
CSS class modifiers use Tailwind-style syntax with -- prefix to control dropdown behavior.
| Class Modifier | Target Element | Values | Default | Description |
| --- | --- | --- | --- | --- |
| [--placement:*] | hs-dropdown (container) | "auto" | "auto-start" | "auto-end" | "top" | "top-left" | "top-right" | "bottom" | "bottom-left" | "bottom-right" | "right" | "right-start" | "right-end" | "left" | "left-start" | "left-end" | "bottom" | Specifies the position of the menu when opened relative to the toggle button. |
| [--scope:*] | hs-dropdown (container) | "window" | "parent" | "parent" | Determines whether the dropdown will be moved outside the parent, for correct display in elements with hidden overflow. Requires the Floating UI plugin. |
| [--auto-close:*] | hs-dropdown (container) | "inside" | "outside" | "false" | "true" | "true" | Specifies the zone, when clicked, which will close the menu. inside closes only when clicking inside, outside closes only when clicking outside, true closes on any click, false prevents auto-close. |
| [--has-autofocus:*] | hs-dropdown (container) | "true" | "false" | "true" | Disables autofocus on the first focusable element when opening a dropdown. Set to "false" to disable autofocus. |
| [--strategy:*] | hs-dropdown (container) | "fixed" | "absolute" | "fixed" | Sets the position strategy. fixed positions relative to viewport, absolute positions relative to the nearest positioned ancestor. |
| [--adaptive:*] | hs-dropdown (container) | "none" | "adaptive" | "adaptive" | Used to disable horizontal position calculations (useful for dropdown menus that span the full width of the screen) while still calculating the vertical position. |
| [--gpu-acceleration:*] | hs-dropdown (container) | "true" | "false" | "false" | Disable/enable position calculation using the transform property. Set to "true" to enable GPU acceleration. |
| [--offset:*] | hs-dropdown (container) | number | 10 | Sets the dropdown's bottom or top offset in pixels. |
| [--flip:*] | hs-dropdown (container) | "true" | "false" | "true" | Flips the menu's placement when it starts to overlap its reference element. Set to "false" to disable flipping. |
| [--trigger:*] | hs-dropdown (container) | "hover" | "click" | "contextmenu" | "click" | Event to trigger a dropdown. hover shows on mouse enter, click shows on click, contextmenu shows on right-click. |
Example:
<div class="hs-dropdown --trigger:hover --placement:top --auto-close:false">
<button class="hs-dropdown-toggle">Hover me</button>
<div class="hs-dropdown-menu">Menu content</div>
</div>Required CSS Classes
These classes define the structure and must be present for the dropdown to function.
| Class | Required On | Purpose |
| --- | --- | --- |
| hs-dropdown | Container wrapper | Groups the toggle and menu elements together |
| hs-dropdown-toggle | Button element | The clickable element that activates the dropdown |
| hs-dropdown-menu | Menu container | The dropdown menu that appears on trigger |
Optional CSS Classes
| Class | Required On | Purpose |
| --- | --- | --- |
| hs-dropdown-toggle-wrapper | Wrapper element | A wrapper for a Dropdown toggle, useful when some other element is placed in the Dropdown toggle. For example, if you want to place a "+" button inside an existing Dropdown toggle button that opens a modal. |
| hs-dropdown-close | Close element | Dropdown close element (can be multiple). Elements with this class will close the dropdown when clicked. |
Tailwind Modifiers
| Name | Description |
| --- | --- |
| hs-dropdown-open:* | The modifier that allows you to set Tailwind classes when the dropdown menu is open. |
JavaScript API
The HSDropdown object is available in the global window object after the plugin is loaded.
Instance Methods
These methods are called on a dropdown instance.
| Method | Parameters | Return Type | Description |
| --- | --- | --- | --- |
| open(target, openedViaKeyboard) | target: VirtualElement \| HTMLElement (optional)openedViaKeyboard: boolean (optional) | boolean | Forces the dropdown menu to open programmatically. Returns true if opened successfully, false otherwise. |
| close(isAnimated) | isAnimated: boolean (optional) | boolean | Forces the dropdown menu to close programmatically. isAnimated indicates whether the dropdown menu is closed with animation. Returns true if closed successfully, false otherwise. |
| forceClearState() | None | void | Destroys dropdown state without removing the instance. Use to reset dropdown to initial state. |
| destroy() | None | void | Destroys the dropdown instance, removes all generated markup, classes, and event listeners. Use when removing dropdown from DOM. |
Static Methods
These methods are called directly on the HSDropdown class.
| Method | Parameters | Return Type | Description |
| --- | --- | --- | --- |
| HSDropdown.getInstance(target, isInstance) | target: HTMLElement \| string (CSS selector)isInstance: boolean (optional) | HSDropdown \| { id: string \| number, element: HSDropdown } \| null | Returns the dropdown instance associated with the target. If isInstance is true, returns collection item object { id, element } where element is the HSDropdown instance. If isInstance is false or omitted, returns the HSDropdown instance directly. Returns null if dropdown instance is not found. |
| HSDropdown.open(target, openedViaKeyboard) | target: HSDropdown \| HTMLElement \| string (CSS selector)openedViaKeyboard: boolean (optional) | void | Opens the dropdown identified by target. Accepts a dropdown instance, DOM element, or CSS selector string. Static alternative to instance open() method. |
| HSDropdown.close(target) | target: HSDropdown \| HTMLElement \| string (CSS selector) | void | Closes the dropdown identified by target. Accepts a dropdown instance, DOM element, or CSS selector string. Static alternative to instance close() method. |
Usage Examples
Example 1: Using instance methods (public API)
// Create a new dropdown instance
const dropdown = new HSDropdown(document.querySelector('#hs-dropdown'));
const openBtn = document.querySelector('#hs-open-btn');
// Open dropdown programmatically
openBtn.addEventListener('click', () => {
dropdown.open();
});Example 2: Using static methods
const openBtn = document.querySelector('#hs-open-btn');
// Open dropdown using static method (no instance needed)
openBtn.addEventListener('click', () => {
HSDropdown.open('#hs-dropdown');
});Example 3: Getting instance and using methods (recommended pattern)
// Get the dropdown instance
const instance = HSDropdown.getInstance('#hs-dropdown', true);
if (instance) {
const { element } = instance; // element is the HSDropdown instance
const openBtn = document.querySelector('#hs-open-btn');
// Use instance methods
openBtn.addEventListener('click', () => {
element.open();
});
// Clean up when removing from DOM
function removeDropdown() {
element.destroy();
}
}Example 4: Destroying dropdown instance
const instance = HSDropdown.getInstance('#hs-dropdown', true);
if (instance) {
const { element } = instance;
const removeBtn = document.querySelector('#hs-remove-btn');
removeBtn.addEventListener('click', () => {
// Clean up before removing from DOM
element.destroy();
// Now safe to remove the element
document.querySelector('#hs-dropdown').remove();
});
}Events
Dropdown instances emit events that can be listened to for lifecycle hooks and custom behavior.
| Event Name | When Fired | Callback Parameter | Description |
| --- | --- | --- | --- |
| on:open | After dropdown menu is opened | HSDropdown (dropdown instance) | Fires after the dropdown menu has been displayed. Use for post-open actions like loading content or tracking analytics. |
| on:close | After dropdown menu is closed | HSDropdown (dropdown instance) | Fires after the dropdown menu has been hidden. Use for cleanup or state updates. |
Event Usage Example
// Get dropdown instance
const instance = HSDropdown.getInstance('#hs-dropdown', true);
if (instance) {
const { element } = instance;
// Listen to open event
element.on('open', (dropdownInstance) => {
console.log('Dropdown opened:', dropdownInstance);
// Perform actions after dropdown opens
// e.g., load content, update UI, track analytics
});
// Listen to close event
element.on('close', (dropdownInstance) => {
console.log('Dropdown closed:', dropdownInstance);
// Perform cleanup or state updates
});
}Common Patterns
Pattern 1: Hover Trigger
Show dropdown on hover instead of click.
<div class="hs-dropdown --trigger:hover">
<button class="hs-dropdown-toggle">Hover me</button>
<div class="hs-dropdown-menu">Menu content</div>
</div>Pattern 2: Programmatic Control
Control dropdown from external buttons.
<div class="hs-dropdown" id="hs-dropdown-first">
<button class="hs-dropdown-toggle">Actions</button>
<div class="hs-dropdown-menu">Menu content</div>
</div>
<button id="hs-open-dropdown">Open Dropdown</button>
<button id="hs-close-dropdown">Close Dropdown</button>
<script>
const instance = HSDropdown.getInstance('#hs-dropdown-first', true);
if (instance) {
const { element } = instance;
document.querySelector('#hs-open-dropdown').addEventListener('click', () => {
element.open();
});
document.querySelector('#hs-close-dropdown').addEventListener('click', () => {
element.close();
});
}
</script>License
Copyright (c) 2026 Preline Labs.
Licensed under the MIT License.
