Tutorials / / 4 min read
How to create a flyout menu with Tailwind CSS: JavaScript and Alpine.js
Build a flyout menu (a dropdown) with Tailwind CSS, two ways: vanilla JavaScript class toggling or Alpine.js with x-show and smooth transitions.
Today we are building a simple flyout menu, AKA a dropdown. We’ll do it twice, first with vanilla JavaScript, then with Alpine.js, so you can pick whichever fits your stack.
What is a flyout menu?
A flyout menu is a menu that appears when the user clicks a button or icon, typically used to display additional information or options related to a specific item or action.
Use cases
- Navigation menus: reveal sub-menus when a user clicks a specific item.
- Product listings: show extra details or actions for a product or category.
- Forms: tuck away secondary fields or options until they are needed.
- Tooltips: offer contextual information next to the element that triggered it.
The markup
id="flyoutButton": the button that toggles the flyout menu.id="flyoutMenu": the element that contains the flyout menu.absolute: positions the menu relative to its nearest positioned ancestor.z-10: keeps the menu on top of other elements.max-w-xs: caps the width so the menu fits within the screen.hidden: the class the script toggles to show and hide the menu.
Classes and irrelevant content are removed for brevity, but I’ll keep those classes relevant to the tutorial.
<!-- Button to toggle the dropdown menu visibility -->
<button id="flyoutButton">I am your flyout menu</button>
<!-- Dropdown Menu -->
<div id="flyoutMenu" class="absolute z-10 hidden max-w-xs ">
<!-- Dr<div>
opdown content goes here -->
</div>The script
document.addEventListener("DOMContentLoaded", function () {: runs when the page is loaded, then grabs the button and the menu.button.addEventListener("click", function (event) {: toggles the menu when the button is clicked.event.stopPropagation();: stops the click from bubbling up, so the document listener doesn’t immediately close the menu.menu.classList.toggle("hidden");: shows or hides the menu.- The
documentclick listener addshiddenback whenever a click lands outside the menu.
document.addEventListener("DOMContentLoaded", function () {
const button = document.getElementById("flyoutButton");
const menu = document.getElementById("flyoutMenu");
button.addEventListener("click", function (event) {
event.stopPropagation(); // Prevent click event from bubbling up
menu.classList.toggle("hidden");
});
document.addEventListener("click", function (event) {
if (!menu.contains(event.target)) {
menu.classList.add("hidden");
}
});
});The Alpine.js version
Same component, no separate script: the state lives in x-data.
x-data="{ open: false }": initializes the component with a reactiveopenproperty that tracks whether the dropdown is visible.@click="open = !open"on the button: togglesopen, showing or hiding the menu.@click.away="open = false": listens for clicks outside the wrapper and closes the dropdown.x-show="open": toggles the visibility of the menu based on the state.
<div
@click.away="open = false"
class="relative flex"
x-data="{ open: false }"
></div>Alpine also makes it easy to animate the menu in and out with x-transition:
x-transition:enter="transition ease-out duration-100": the enter transition, 100 milliseconds with an ease-out timing.x-transition:enter-start="transform opacity-0 scale-95"andx-transition:enter-end="transform opacity-100 scale-100": the menu starts slightly scaled down and transparent, then grows to full size and opacity.x-transition:leave="transition ease-in duration-75"with itsleave-startandleave-endvalues: the same in reverse, fading and scaling the menu out over 75 milliseconds.
<div
x-show="open"
x-transition:enter="transition ease-out duration-100"
x-transition:enter-start="transform opacity-0 scale-95"
x-transition:enter-end="transform opacity-100 scale-100"
x-transition:leave="transition ease-in duration-75"
x-transition:leave-start="transform opacity-100 scale-100"
x-transition:leave-end="transform opacity-0 scale-95"
class="...."
></div>And the Tailwind classes that position the dropdown:
absolute z-10: positions the menu absolutely and keeps it above other content.w-screen max-w-xs px-2 mt-3: sizes the menu and spaces it away from the toggle button.transform -translate-x-1/2 left-1/2: centers the dropdown horizontally relative to its parent.
<div
x-show="open"
x-transition:enter="..."
x-transition:enter-start="..."
x-transition:enter-end="..."
x-transition:leave="..."
x-transition:leave-start="..."
x-transition:leave-end="..."
class="absolute z-10 w-screen max-w-xs px-2 mt-3 transform -translate-x-1/2 left-1/2"
></div>You can try the Alpine version separately: live demo and source code.
Which one should you use?
If Alpine is already in your project, x-show with x-transition gives you the click-away handling and smooth animations for free. If not, the vanilla version is a few lines and no dependency.
Conclusion
This is a simple flyout menu and a great starting point for building more complex ones; check out the other tutorials for more advanced examples.
Hope you enjoyed this tutorial and have a great day!
/Michael Andreuzza