Skip to content

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:

blade
<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:

blade
<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:

blade
<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:

blade
<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:

blade
<!-- ✅ 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
blade
<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
blade
<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

blade
<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

blade
<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

blade
<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>
blade
<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

blade
<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

blade
<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

blade
<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