Smooth Show/Hide in Alpine.js Using x-show and x-transition
Use x-show with x-transition to animate visibility in Alpine.js while keeping elements in the DOM.
11 Aug 2026, 05:57 UTC

The problem: panels snap open and closed
You click a button and a menu appears instantly. No fade, no slide—just a hard cut. If you try to fix it by removing the element from the DOM when it closes, the animation never runs because there is nothing left to animate.
The practical fix is to keep the element in the DOM and toggle its visibility with Alpine.js's x-show directive, then layer x-transition on top so Alpine applies CSS classes as the element enters and leaves.
x-show binds a Boolean to an element's display property. When the value is false, Alpine sets display: none. The element stays in the markup, so it can still participate in transitions. x-transition tells Alpine to add classes such as x-enter and x-leave at the start and end of each visibility change.
x-show versus removing the node
Some frameworks encourage conditional rendering: if the flag is false, the element is destroyed and recreated. That approach removes the node from the DOM, so there is no element to animate on exit. x-show leaves the node in place and only changes display. This means the element remains available for animation, though it occupies no layout space while hidden.
Worked example: slide-down menu
This example assumes Alpine 3.x and Tailwind CSS utility classes. If you are not using Tailwind, replace the utility classes with your own CSS.
<div x-data="{ open: false }">
<button @click="open = !open" type="button">
Toggle menu
</button>
<div
x-show="open"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 -translate-y-2"
x-transition:enter-end="opacity-100 translate-y-0"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 translate-y-0"
x-transition:leave-end="opacity-0 -translate-y-2"
class="fixed top-4 right-4 w-64 bg-white rounded-lg shadow-lg"
>
<a href="#" class="block px-4 py-2">Profile</a>
<a href="#" class="block px-4 py-2">Settings</a>
</div>
</div>
x-show="open" toggles the menu's visibility. The enter-start classes describe the first frame of the opening animation, and enter-end describes the final frame. The leave-* modifiers do the same in reverse when open becomes false. Alpine applies and removes these classes automatically; you only write the CSS classes.
Verify the behavior in DevTools
- Open the page in a browser and open DevTools to the Elements panel.
- Click the toggle button.
- Confirm the menu
divremains in the DOM tree while hidden. It should carry an inlinestyleattribute withdisplay: none. - Click again and watch the class attribute on the menu
div. Transition classes such asopacity-0and-translate-y-2should appear briefly during the leave phase. - If no classes appear, confirm the Alpine script loads before your component markup and that
x-datais declared on a parent element.
Trade-offs and limits
- Layout space: Because
x-showusesdisplay: none, the element occupies no layout space while hidden. Siblings will shift up as if the element is gone, even though the node remains in the DOM. - Reduced motion: Alpine does not automatically respect
prefers-reduced-motion. Add a media query that disables transitions for users who request reduced motion. - Duration: Keep animations under roughly 300 milliseconds. Long or complex transitions can make the interface feel sluggish and increase main-thread work.
- Accessibility: Elements with
display: noneare removed from the accessibility tree, which is usually the correct behavior for a hidden menu.
Closing
Start with x-show and x-transition for simple visibility toggles. Reserve DOM removal for cases where you truly need to unmount state or event listeners. Verify with DevTools that classes are applied and the node stays in the document. If motion causes discomfort, add a prefers-reduced-motion override. There is no server state to roll back; revert the HTML file if the new behavior causes issues.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.