Conditional Rendering
Conditional rendering allows you to show or hide parts of your interface based on signal values. Datastar provides data-show for CSS-based visibility control, while Hyper extends this with data-if for template-based conditional rendering. Understanding when to use each approach helps you build efficient, maintainable interfaces.
CSS-Based Visibility
data-show (Datastar)
The data-show attribute controls element visibility using CSS display property:
<div data-signals="{isLoggedIn: false}">
<div data-show="$isLoggedIn">
Welcome back! You're logged in.
</div>
<div data-show="!$isLoggedIn">
Please log in to continue.
</div>
</div>When the condition is true, the element displays normally. When false, Datastar sets display: none.
How It Works
data-show toggles CSS visibility:
- True: Removes
display: none(or restores previous display value) - False: Sets
display: none
The element remains in the DOM at all times—only its visibility changes.
Expressions
Use any JavaScript expression:
<div data-signals="{count: 5}">
<div data-show="$count > 0">
You have <span data-text="$count"></span> items
</div>
<div data-show="$count === 0">
Your cart is empty
</div>
<div data-show="$count >= 10">
Bulk discount available!
</div>
</div>Multiple Conditions
Combine conditions with logical operators:
<div data-signals="{
isLoggedIn: true,
isPremium: false,
hasVerifiedEmail: true
}">
<div data-show="$isLoggedIn && $isPremium">
Premium features
</div>
<div data-show="$isLoggedIn && !$hasVerifiedEmail">
Please verify your email
</div>
<div data-show="!$isLoggedIn || !$isPremium">
Upgrade to premium
</div>
</div>Template-Based Rendering
data-if (Hyper)
The data-if attribute conditionally renders elements in the DOM. Unlike data-show, elements are added or removed from the DOM entirely:
<div data-signals="{showModal: false}">
<template data-if="$showModal">
<div class="modal">
<h2>Modal Title</h2>
<p>Modal content here</p>
<button data-on:click="$showModal = false">Close</button>
</div>
</template>
<button data-on:click="$showModal = true">Open Modal</button>
</div>Requirements
data-if must be used on a <template> element with a single root element:
<!-- ✅ Correct - single root element -->
<template data-if="$condition">
<div>
<h1>Title</h1>
<p>Content</p>
</div>
</template>
<!-- ❌ Wrong - multiple root elements -->
<template data-if="$condition">
<h1>Title</h1>
<p>Content</p>
</template>How It Works
When the condition changes:
- True → False: Element is removed from DOM
- False → True: Element is created and inserted into DOM
The template itself remains in the DOM but is hidden with display: none.
data-show vs data-if
Performance Implications
data-show (CSS-based)
- Element always in DOM
- Fast toggle (just CSS change)
- Preserves element state (form inputs, scroll position)
- Uses more memory if element is complex
- Initialization cost paid once
data-if (DOM-based)
- Element added/removed from DOM
- Slower toggle (DOM manipulation)
- Element state is lost when removed
- Lower memory usage when hidden
- Initialization cost paid each time it's shown
When to Use data-show
Use data-show when:
- Toggling frequently (tabs, dropdowns, accordions)
- Need to preserve form input state
- Element is small/simple
- Initial render cost is acceptable
<div data-signals="{activeTab: 'profile'}">
<!-- Tabs toggled frequently - use data-show -->
<div data-show="$activeTab === 'profile'" class="tab-content">
Profile settings...
</div>
<div data-show="$activeTab === 'security'" class="tab-content">
Security settings...
</div>
<div data-show="$activeTab === 'notifications'" class="tab-content">
Notification settings...
</div>
</div>When to Use data-if
Use data-if when:
- Conditionally showing large, complex components
- Element rarely appears (modals, error states)
- Want to reduce initial page weight
- Don't need to preserve state
<div data-signals="{showLargeChart: false}">
<!-- Complex component that's rarely shown - use data-if -->
<template data-if="$showLargeChart">
<div class="chart-container">
<canvas id="complexChart"></canvas>
<!-- Lots of chart configuration... -->
</div>
</template>
<button data-on:click="$showLargeChart = true">Load Chart</button>
</div>Common Patterns
Authentication States
<div @signals(['user' => $user])>
<!-- Guest content -->
<div data-show="!$user">
<a href="/login">Log In</a>
<a href="/register">Register</a>
</div>
<!-- Authenticated content -->
<div data-show="$user">
Welcome, <span data-text="$user.name"></span>!
<a href="/logout">Log Out</a>
</div>
</div>Loading States
<div data-signals="{
isLoading: false,
data: null,
error: null
}">
<button
data-on:click="$isLoading = true; @get('/data')"
data-show="!$isLoading">
Load Data
</button>
<div data-show="$isLoading">
Loading...
</div>
<div data-show="$data && !$isLoading">
<div data-text="$data.message"></div>
</div>
<div data-show="$error" class="text-red-500">
Error: <span data-text="$error"></span>
</div>
</div>Multi-Step Forms
<div data-signals="{step: 1, formData: {}}">
<div data-show="$step === 1">
<h2>Step 1: Personal Info</h2>
<input data-bind="formData.name" placeholder="Name" />
<button data-on:click="$step = 2">Next</button>
</div>
<div data-show="$step === 2">
<h2>Step 2: Contact Info</h2>
<input data-bind="formData.email" placeholder="Email" />
<button data-on:click="$step = 1">Back</button>
<button data-on:click="$step = 3">Next</button>
</div>
<div data-show="$step === 3">
<h2>Step 3: Review</h2>
<p>Name: <span data-text="$formData.name"></span></p>
<p>Email: <span data-text="$formData.email"></span></p>
<button data-on:click="$step = 2">Back</button>
<button data-on:click="@postx('/submit')">Submit</button>
</div>
</div>Modal Dialog
<div data-signals="{isModalOpen: false, modalData: null}">
<!-- Trigger -->
<button data-on:click="$isModalOpen = true; $modalData = {title: 'Hello', message: 'World'}">
Open Modal
</button>
<!-- Modal (use data-if since it's heavy and rarely shown) -->
<template data-if="$isModalOpen">
<div class="modal-backdrop" data-on:click="$isModalOpen = false">
<div class="modal-content">
<h2 data-text="$modalData.title"></h2>
<p data-text="$modalData.message"></p>
<button data-on:click="$isModalOpen = false">Close</button>
</div>
</div>
</template>
</div>Conditional Sections
<div data-signals="{
userType: 'individual',
showAdvanced: false
}">
<select data-bind="userType">
<option value="individual">Individual</option>
<option value="business">Business</option>
</select>
<!-- Show/hide based on user type -->
<div data-show="$userType === 'business'">
<input data-bind="companyName" placeholder="Company Name" />
<input data-bind="taxId" placeholder="Tax ID" />
</div>
<!-- Advanced options (heavy component, use data-if) -->
<button data-on:click="$showAdvanced = !$showAdvanced">
<span data-text="$showAdvanced ? 'Hide' : 'Show'"></span> Advanced Options
</button>
<template data-if="$showAdvanced">
<div class="advanced-options">
<!-- Complex configuration UI... -->
</div>
</template>
</div>Empty States
<div @signals(['items' => []])>
<template data-for="item in $items" data-for__key.id="item in $items">
<div data-text="item.name"></div>
</template>
<div data-show="$items.length === 0" class="empty-state">
<p>No items found</p>
<button data-on:click="@get('/reload')">Reload</button>
</div>
</div>Permission-Based UI
<div @signals([
'user' => $user,
'canEdit' => $user->can('edit'),
'canDelete' => $user->can('delete')
])>
<div data-show="$canEdit">
<button data-on:click="@get('/edit')">Edit</button>
</div>
<div data-show="$canDelete">
<button data-on:click="@deletex('/delete')" class="text-red-500">
Delete
</button>
</div>
<div data-show="!$canEdit && !$canDelete">
<p class="text-gray-500">You don't have permission to modify this</p>
</div>
</div>Learn More
- Display & Binding - Show dynamic content conditionally
- Styling - Apply conditional classes and styles
- Computed & Effects - Derive conditional logic from signals
- Datastar Documentation - Complete reference for
data-show

