Conditional Rendering with {#if}
Showing and hiding markup based on state using if, else if, and else blocks.
2 min de lecture
Curly braces in Svelte markup only accept expressions, not statements — you can write {isLoggedIn} but not {if (isLoggedIn) { ... }}. For actual control flow, Svelte has its own block syntax, opening with # and closing with /. The most common one is {#if}.
<script>
let isLoggedIn = $state(false);
</script>
{#if isLoggedIn}
<p>Welcome back!</p>
{:else}
<p>Please log in.</p>
{/if}{#if ...} opens the block, {:else} marks the alternative branch, and {/if} closes it. Whichever branch doesn't match the condition simply isn't rendered — its DOM nodes don't exist at all, rather than existing but hidden with CSS.
Chaining conditions
<script>
let status = $state('loading');
</script>
{#if status === 'loading'}
<p>Loading…</p>
{:else if status === 'error'}
<p>Something went wrong.</p>
{:else if status === 'empty'}
<p>No results found.</p>
{:else}
<p>Here are your results.</p>
{/if}{:else if ...} chains as many additional conditions as you need, evaluated top to bottom — the first one that matches is the branch that renders, exactly like a JavaScript if/else if/else chain.
Why not just hide it with CSS?
You could get a similar visual effect with display: none, toggled by a class:
<p class:hidden={!isLoggedIn}>Welcome back!</p>But that keeps the element (and anything inside it) mounted in the DOM at all times — its $effects still run, its lifecycle hooks still fire, and any expensive child component still exists and does work even while invisible. {#if} actually creates and destroys the DOM nodes and component instances as the condition changes, which is usually what you want: a logged-out view shouldn't be quietly running the logic meant only for a logged-in one.
Reach for class:hidden-style toggling only when you specifically want the element to stay mounted — for instance, a CSS transition that animates something out of view rather than removing it instantly.
Conditions can wrap any markup
{#if} isn't limited to text — it can wrap components, multiple sibling elements, or nothing at all:
{#if user}
<UserProfile {user} />
<LogoutButton />
{/if}Both <UserProfile> and <LogoutButton> mount together when user becomes truthy, and unmount together when it doesn't — there's no need to repeat the condition on each one individually.