Description: Understand and use x-if (template added/removed from DOM) or x-show (CSS display: none;) appropriately. x-if is generally better for expensive or rarely shown content, while x-show is preferred for frequently toggled elements where preserving state or performance of frequent toggles is key.
x-if: This directive conditionally renders a block of HTML. When the expression provided to x-if evaluates to true, the content of the associated <template> tag is cloned and inserted into the DOM. When it evaluates to false, the element is removed from the DOM entirely.
Example:
<template x-if="isAdmin">
<div>Admin Controls</div>
</template>
x-show: This directive toggles the visibility of an element by manipulating its CSS display property (typically setting it to none when false). The element always remains in the DOM, even when hidden. This is useful for elements that are toggled frequently, as it avoids the cost of DOM manipulation and preserves the internal state of the element and its children (e.g., input values, component states).
Example:
<div x-show="isOpen">
Dropdown Content
</div>
<template> tag (for x-if): The x-if directive must be used on a <template> tag. This tag serves as an invisible wrapper for the conditional content. Alpine.js uses the content inside the <template> as a blueprint to create or remove elements from the DOM. The <template> tag itself is not rendered.
x-if requires a <template> tag: A common mistake is to apply x-if directly to a <div> or other visible element. This will not work as expected for dynamic rendering because Alpine needs the <template> to know what to clone and insert/remove.
Incorrect: <div x-if="condition">...</div>
Correct: <template x-if="condition"><div>...</div></template>
Component Lifecycle and State Preservation:
x-if: Components (elements with x-data, x-init, etc.) within an x-if block are destroyed when the condition becomes false and re-initialized when it becomes true again. This means their init() functions (and any x-init directives) are re-run, and any internal state (like input values or reactive data properties) is lost and reset. This can be beneficial for "heavy" components that you don't want to keep in memory, but detrimental if you need to preserve state.
x-show: Components within an x-show block are initialized once when their parent Alpine component loads. Their state is preserved even when hidden because they remain in the DOM. The init() function or x-init directive runs only once. This is ideal for frequently toggled UI elements where state preservation (e.g., form inputs, scroll positions, child component states) is important, or where the re-initialization cost is undesirable.
This difference has significant implications for performance, how you structure your initialization logic (e.g., in x-init or a component's init() method), and any cleanup logic (e.g., in a component's destroy() method, which would be called for x-if but not for x-show during toggling).
This example demonstrates the difference between x-if and x-show. Pay attention to the "Initialization Messages", "Global Init Counts", and the behavior of the input fields when you toggle the sections.
Open your browser's developer console to see log messages related to initialization.
x-if ExampleVisibility:
x-ifThis block is completely removed from/added to the DOM.
Initialized at:
Global x-if init count:
Text is lost on toggle off/on.
x-if content is currently removed from the DOM.
x-show ExampleVisibility:
x-showVisibility toggled via CSS display: none;. Stays in DOM.
Initialized at:
Global x-show init count:
Text is preserved on toggle off/on.
x-show content is currently hidden (display: none).