Skip to content

Template Syntax

Any {expression} in markup embeds a JavaScript value. Because Svelte tracks reactivity through runes, when the expression depends on reactive state, the compiler updates that spot automatically.

<script>
let user = $state({ name: 'Ada', points: 42 });
</script>
<p>{user.name} has {user.points} points</p>
<p>{user.points > 40 ? 'VIP' : 'member'}</p> <!-- any JS expression -->
<img src={user.avatar} alt={user.name} /> <!-- in attributes too -->

Unlike JSX (which uses JavaScript && and .map()), Svelte has dedicated block syntax for control flow. It reads like HTML and the compiler optimizes it directly.

Conditionals — {#if}:

{#if loggedIn}
<p>Welcome back</p>
{:else if guest}
<p>Browsing as guest</p>
{:else}
<button onclick={login}>Log in</button>
{/if}

Lists — {#each} (with a keyed identity via the (item.id) expression, so the compiler tracks items across reorders):

{#each todos as todo (todo.id)}
<li>{todo.text}</li>
{:else}
<li>No todos yet</li> <!-- rendered when the list is empty -->
{/each}

Async — {#await} handles a promise’s pending, resolved, and rejected states inline:

{#await fetchUser()}
<p>Loading…</p>
{:then user}
<p>Hello {user.name}</p>
{:catch error}
<p>Failed: {error.message}</p>
{/await}
flowchart TB
  tmpl["Svelte markup"] --> ifb["{#if} conditional branches"]
  tmpl --> each["{#each} lists (keyed by id)"]
  tmpl --> awaitb["{#await} promise states"]
  tmpl --> key["{#key} destroy and recreate on change"]
The template blocks and what each expresses

The {#key expression} block destroys and recreates its contents whenever the expression changes — useful to force a re-mount (resetting state or replaying a transition).

  • {@html string} injects a raw HTML string (bypassing escaping). Use it only for trusted/sanitized content — it’s an XSS vector otherwise.
  • {@render snippet()} renders a snippet — Svelte 5’s reusable markup block that replaces the old <slot> mechanism. Snippets get a full lesson later; for now, know that {@render …} is how snippet content lands in the markup.
{@html sanitizedMarkdown}
{#snippet greeting(name)}
<p>Hello {name}</p>
{/snippet}
{@render greeting('world')}
How does Svelte express conditional rendering?
Why add `(todo.id)` to an `{#each todos as todo (todo.id)}` block?
What does `{#await promise}` do?
What is `{@render snippet()}` for?