Description: Understand and use x-if (template added/removed from DOM) or x-show (CSS display: none;) appropriately. x-if is better for expensive/rare content; x-show for frequent toggles.
Conditional rendering is a fundamental concept in dynamic web applications. AlpineJS provides two primary directives for this: x-if and x-show. While both control the visibility of elements, they do so in fundamentally different ways, leading to different performance characteristics and use cases.
x-if F="condition"F: This directive conditionally renders a block of HTML.
If the JavaScript expression provided in condition evaluates to true, the content inside the associated <template> tag is cloned and inserted into the DOM.
If the expression evaluates to false, the content is completely removed from the DOM.
x-show F="condition"F: This directive toggles the visibility of an element using CSS.
The element always remains in the DOM. If the condition is true, the element is visible (Alpine removes any style="display: none;"). If the condition is false, Alpine applies style="display: none;" to the element, hiding it.
x-show also seamlessly integrates with Alpine's transition directives like x-transition.
<template> tag (for x-if): The x-if directive must be placed on an HTML <template> tag. This tag acts as an invisible container for the HTML content that x-if will manage. The <template> tag itself is not rendered in the DOM; only its contents are processed by x-if.
<!-- Correct usage of x-if -->
<template x-if="isAdmin">
<div>Admin Controls</div>
</template>
x-if when:
x-init) each time they are shown, or properly cleaned up (e.g., running x-destroy) when hidden.x-show when:
x-transition directives for smooth animations, as they work best with x-show.x-if requires a <template> tag: A very common mistake is to put x-if directly on a <div> or other visible HTML element. This will not work as intended for DOM addition/removal. x-if specifically looks for its content to be wrapped in a <template> element.
<!-- Correct -->
<template x-if="user.isLoggedIn">
<p>Welcome, <span x-text="user.name"></span>!</p>
</template>
<!-- Incorrect: x-if on a div will not properly manage DOM presence -->
<!-- <div x-if="user.isLoggedIn">This is problematic.</div> -->
Component Lifecycle: `x-if` re-initializes, `x-show` preserves: This is a critical distinction with significant implications.
x-if: When the condition becomes true, any Alpine components or elements with x-init directives *within* the <template> are newly created and initialized. Their init() methods (or x-init expressions) run. When the condition becomes false, these components are destroyed, their destroy() methods (or x-destroy expressions) run, and all their internal state is lost. If they are shown again, it's a fresh start.x-show: Alpine components or elements with x-init within an element controlled by x-show are initialized *once* when their parent Alpine component loads (assuming they are not themselves within an x-if). Their state is preserved even when they are hidden and re-shown via x-show. The x-init logic runs only on the initial load, not on subsequent toggles. x-destroy only runs if the element itself or its parent is removed from the DOM (e.g., by an outer x-if).This difference impacts:
x-if can be slower due to repeated DOM manipulation and component re-initialization. x-show is generally faster for frequent toggles.init() / x-init): If you have setup logic (e.g., API calls within a sub-component's init, event listener setup) it will re-run every time for x-if, but only once for x-show.destroy() / x-destroy): Essential for preventing memory leaks if you set up things like global event listeners or timers within a component. x-if ensures this cleanup happens on hide. With x-show, cleanup would typically only happen when the parent component is destroyed.x-if but maintained with x-show.x-if Example: Complex ModalThis modal is only added to the DOM when shown. Its content and component (if any) are re-initialized each time.
Modal has been opened times.
This content is truly removed from the DOM when hidden.
Any Alpine component state inside me (like this counter) resets each time I open.
Internal counter: (resets on re-open)
Initialized count (from parent):
Try closing and re-opening. Check the console and the log below.
x-show Example: Quick Info PanelThis panel is always in the DOM, just hidden with CSS. Its state is preserved across toggles.
I am always in the DOM. My x-init ran only once.
Initialized at:
Current input: