Description: Completely add or remove a block of HTML from the DOM based on a data condition. The HTML must be nested within a <template> tag.
The x-if directive in AlpineJS is designed to conditionally render (i.e., add or remove) blocks of HTML directly from/to the Document Object Model (DOM). A critical aspect of x-if is that it must be placed on an HTML <template> tag. The content nested inside this <template> tag is what AlpineJS will manage based on a JavaScript condition.
The basic syntax is as follows:
<template x-if="yourJavaScriptCondition">
<div>
<!-- This content is rendered only if 'yourJavaScriptCondition' is true -->
<p>Visible when condition is met!</p>
</div>
</template>
Let's break this down:
<template ...>: This is an HTML element that holds content not to be rendered immediately by the browser. It serves as an inert container for the HTML structure that x-if will control.x-if="yourJavaScriptCondition":
x-if directive is attached to the <template> tag.yourJavaScriptCondition is any JavaScript expression that evaluates to a boolean (true or false). This expression has access to the data properties of its Alpine.js component.<template>: This is the HTML markup (e.g., <div>...</div> in the example) that gets cloned and inserted into the DOM immediately after the <template> tag if yourJavaScriptCondition evaluates to true. If the condition is false, this content is either not inserted or, if already present, is completely removed from the DOM.When the condition becomes true, Alpine effectively takes a copy of the template's inner HTML and renders it. When false, that rendered copy is destroyed. The <template> tag itself always remains in the DOM (though it's invisible and doesn't affect layout), acting as an anchor point for Alpine.
Coming from Python environments, where templating often happens server-side, there are a few client-side specifics with x-if to be mindful of:
x-if Content in a <template> TagThis is a fundamental requirement and a common oversight. The x-if directive exclusively operates on a <template> tag. The purpose of the <template> is to hold a blueprint of HTML that Alpine can instantiate or discard.
Placing x-if directly on a non-template element like a <div> will not achieve the desired conditional DOM rendering:
<!-- INCORRECT: x-if on a div for DOM manipulation -->
<div x-if="isShown">This content will not be added/removed from the DOM by x-if.</div>
<!-- CORRECT: x-if on a template tag -->
<template x-if="isShown">
<div>This content WILL be added/removed from the DOM.</div>
</template>
AlpineJS will not treat an x-if on a regular element as a trigger for adding/removing that element from the DOM. The directive is processed, but not in the way intended for conditional rendering of the element itself.
x-if with x-showBoth directives control element visibility based on a condition, but their mechanisms and implications are distinct:
x-if:
false and re-creates and inserts them when the condition becomes true.x-show:
display: none; CSS style on the element. The element always remains in the DOM.For example, if you have an input field inside a conditional block, x-if would clear its value when hidden and re-shown, while x-show would preserve it.
x-if Block is Destroyed When HiddenThis is a direct consequence of x-if removing elements from the DOM. Any local state within the conditional block, including the state of nested Alpine.js components or native HTML element states (like form input values or scroll positions), is lost when the x-if condition evaluates to false.
When the condition becomes true again, Alpine re-evaluates the template content, effectively creating a fresh instance of that HTML structure. Any Alpine components defined within that structure will be re-initialized with their default state.
<div x-data="{ showDetails: true }">
<button @click="showDetails = !showDetails">Toggle Details</button>
<template x-if="showDetails">
<div x-data="{ counter: 0 }" x-init="console.log('Detail component initialized')">
<p>Counter: <span x-text="counter"></span></p>
<button @click="counter++">Increment Counter</button>
<input type="text" placeholder="Enter text here">
</div>
</template>
</div>
In the example above, if you increment the counter or type text into the input, then toggle showDetails to false and back to true, the counter will reset to 0, the "Detail component initialized" message will log again, and the input field will be empty. This is because the inner div with its own x-data is completely rebuilt.
If state preservation is necessary while an element is hidden, x-show is the appropriate directive to use.
Current error state (hasError property):
Oh no! An Error Occurred!
This message is rendered using x-if="hasError". When 'hasError' is false, this entire block is removed from the DOM. Try inspecting the DOM elements!
All Clear!
No error to display. The error message block (controlled by x-if="hasError") is currently not in the DOM.
Open your browser's developer tools (usually F12 or Right-click > Inspect) and navigate to the "Elements" or "Inspector" panel. Observe the HTML structure as you click the button. You'll see AlpineJS adds comments like <!-- ALPINE-IF-CONTENT --> and <!-- /ALPINE-IF-CONTENT --> around the conditionally rendered block when it's present. When hasError is false, the entire error message div (and these comments) will disappear from the DOM, leaving only the <template> tag itself.