Published 12/5/2025 · 4 min read
Tags: svelte , templates , conditionals
Lesson 6: Conditional Rendering
Not everything should show all the time. Svelte’s {#if} blocks let you conditionally render parts of your UI based on state.
Basic If Blocks
<script>
let loggedIn = false
</script>
{#if loggedIn}
<p>Welcome back!</p>
{/if}
<button onclick={() => loggedIn = !loggedIn}>
{loggedIn ? 'Log out' : 'Log in'}
</button>
When loggedIn is true, the paragraph appears. When it’s false, it’s not in the DOM at all — not hidden, completely absent.
If-Else
{#if loggedIn}
<Dashboard />
{:else}
<LoginForm />
{/if}
Note the syntax: {:else} uses a colon because it continues the block started by {#if}.
If-Else-If Chains
<script>
let status = 'pending'
</script>
{#if status === 'loading'}
<Spinner />
{:else if status === 'error'}
<ErrorMessage />
{:else if status === 'success'}
<Results />
{:else}
<EmptyState />
{/if}
You can chain as many {:else if} blocks as you need.
The Syntax Pattern
Svelte’s template blocks follow a consistent pattern:
{#starts a block{:continues a block{/ends a block
So {#if}, {:else}, {/if}.
Truthy and Falsy Values
Svelte uses JavaScript’s truthy/falsy rules:
<script>
let items = []
let user = null
let count = 0
</script>
{#if items.length}
<ItemList {items} />
{:else}
<p>No items yet</p>
{/if}
{#if user}
<p>Hello, {user.name}</p>
{/if}
{#if count}
<p>Count: {count}</p>
{/if}
Falsy values: false, 0, '', null, undefined, NaN
Everything else is truthy, including empty arrays and objects.
Nested Conditions
You can nest if blocks:
{#if user}
{#if user.isAdmin}
<AdminPanel />
{:else}
<UserDashboard />
{/if}
{:else}
<LoginPrompt />
{/if}
But consider if this is readable. Sometimes separate components or computed values are clearer:
<script>
let user = { isAdmin: true }
$: showAdmin = user && user.isAdmin
</script>
{#if !user}
<LoginPrompt />
{:else if showAdmin}
<AdminPanel />
{:else}
<UserDashboard />
{/if}
Comparing to Vue
Vue’s conditional directives:
<p v-if="loggedIn">Welcome back!</p>
<p v-else>Please log in</p>
<div v-if="status === 'loading'">Loading...</div>
<div v-else-if="status === 'error'">Error!</div>
<div v-else>Done</div>
Svelte’s approach:
{#if loggedIn}
<p>Welcome back!</p>
{:else}
<p>Please log in</p>
{/if}
{#if status === 'loading'}
<div>Loading...</div>
{:else if status === 'error'}
<div>Error!</div>
{:else}
<div>Done</div>
{/if}
Vue attaches conditions to elements. Svelte wraps elements in condition blocks. Both work well — it’s a matter of taste.
One advantage of Svelte’s approach: you can conditionally render multiple elements without a wrapper:
{#if showDetails}
<h2>Details</h2>
<p>Some details here</p>
<p>More details here</p>
{/if}
In Vue, you’d need a wrapper element or <template>.
Common Patterns
Loading states:
<script>
let loading = true
let data = null
let error = null
async function loadData() {
loading = true
error = null
try {
const response = await fetch('/api/data')
data = await response.json()
} catch (e) {
error = e.message
} finally {
loading = false
}
}
</script>
{#if loading}
<div class="spinner">Loading...</div>
{:else if error}
<div class="error">
<p>Something went wrong: {error}</p>
<button onclick={loadData}>Try again</button>
</div>
{:else if data}
<DataDisplay {data} />
{:else}
<p>No data available</p>
{/if}
Feature flags:
<script>
export let features = {
darkMode: true,
betaFeatures: false
}
</script>
{#if features.darkMode}
<DarkModeToggle />
{/if}
{#if features.betaFeatures}
<BetaWarning />
<ExperimentalFeature />
{/if}
Permission checks:
<script>
export let user
$: canEdit = user?.permissions?.includes('edit')
$: canDelete = user?.permissions?.includes('delete')
</script>
{#if canEdit}
<button onclick={handleEdit}>Edit</button>
{/if}
{#if canDelete}
<button onclick={handleDelete}>Delete</button>
{/if}
Form validation feedback:
<script>
let email = ''
let touched = false
$: isValid = email.includes('@')
$: showError = touched && !isValid
</script>
<input
type="email"
bind:value={email}
onblur={() => touched = true}
/>
{#if showError}
<p class="error">Please enter a valid email</p>
{/if}
Conditional Classes and Styles
For simple visibility toggling, sometimes CSS is better:
<script>
let visible = true
</script>
<!-- Remove from DOM -->
{#if visible}
<div>I disappear completely</div>
{/if}
<!-- Hide with CSS -->
<div class:hidden={!visible}>
I stay in the DOM but become invisible
</div>
<style>
.hidden {
display: none;
}
</style>
Use {#if} when:
- The content is expensive to render
- You want to unmount components (trigger cleanup)
- The content shouldn’t be in the DOM at all (accessibility, SEO)
Use CSS when:
- You’re toggling frequently and want to avoid re-renders
- You need transitions on the element
- The content should remain in the DOM (form inputs keeping values)
Key Takeaways
{#if},{:else if},{:else},{/if}control conditional rendering- Content inside false conditions is removed from the DOM entirely
- JavaScript truthy/falsy rules apply
- Multiple elements can be wrapped without needing a container
- Consider computed values (
$:) to simplify complex conditions - CSS hiding is sometimes better than conditional rendering
Related Articles
- x402 with SvelteKit: Full-Stack Example
Build a complete SvelteKit application with x402 payments - wallet connection, protected routes, and automatic payment handling.
- Interacting with Programs from Svelte
Build Svelte components that interact with Solana programs - token balances, transfers, and real-time updates.
- Signing Messages and Transactions in the Browser
Learn to sign messages for authentication and build transactions that users approve through their wallet.