Conditional and List Rendering

Chapter 5
Conditional and List Rendering
Logic blocks in the markup — {#if}, {#each}, and the rather neat {#await}

Svelte handles conditionals and lists with logic blocks — special {#...} markers in the markup, closed with {/...}. This is a distinct style from all three other frameworks: not JavaScript-in-JSX (React), not directives-on-elements (Vue/Angular), but dedicated block syntax that reads almost like a templating language. Svelte even has a block for handling promises directly, which the others lack.

{#if} / {:else if} / {:else}

{#if status === 'loading'} <p>Loading...</p> {:else if status === 'error'} <p>Something went wrong.</p> {:else} <p>Ready!</p> {/if}

An {#if} block conditionally renders its contents, with optional {:else if} and {:else} branches. The opening tag uses #, continuation branches use :, and the close uses / — a consistent convention across all Svelte blocks. Like Angular's @if and Vue's v-if, a false branch genuinely removes its elements from the DOM. The whole {#if}/{:else if}/{:else} chain cleanly handles the loading/error/success pattern.

{#each} — Rendering a List

<ul> {#each fruits as fruit} <li>{fruit}</li> {/each} </ul>

{#each items as item} repeats its contents once per array entry — Svelte's .map() equivalent. For lists that change (items added, removed, reordered), you should provide a key in parentheses so Svelte can track each item efficiently — the same role as React's key, Vue's :key, and Angular's track:

{#each todos as todo (todo.id)} <li>{todo.text}</li> {/each}

The (todo.id) after the item is the key. Unlike Angular's mandatory track, it's optional in Svelte — but strongly recommended for any list that mutates, for exactly the same correctness reasons.

{#each} with Index and an Empty Fallback

{#each todos as todo, index (todo.id)} <li>{index + 1}. {todo.text}</li> {:else} <li>No todos yet.</li> {/each}

A second variable after the item gives the index — todo, index. And neatly, {#each} supports its own {:else} branch, rendered when the array is empty — handling the "empty list" case inline, the same convenience as Angular's @empty but built right into the each-block.

{#await} — Rendering a Promise Directly

{#await promise} <p>Loading...</p> {:then data} <p>Got: {data.title}</p> {:catch error} <p>Failed: {error.message}</p> {/await}

This block has no equivalent in the other frameworks. {#await promise} renders the three states of a Promise directly in the markup: pending (before {:then}), resolved ({:then data} with the result), and rejected ({:catch error}). The loading/error/success pattern that took manual status state in every other framework's data-fetching is handled declaratively here, with the promise itself as the source of truth. (The {:then} and {:catch} branches are both optional if you only care about some states.)

{#await} is genuinely distinctive
Every other framework makes you manage isLoading/error/data state by hand (or reach for a library like React Query). Svelte bakes the three-state promise lifecycle into a template block. Pass a fetch promise straight in — {#await fetch(url).then(r => r.json())} — and the markup handles all three states with no extra state variables at all.
No v-if + v-for problem here — but key your dynamic lists
Svelte doesn't have Vue's "don't combine v-if and v-for" issue (the blocks nest cleanly). The one thing to stay disciplined about is keying: an unkeyed {#each} over a list that gets reordered or filtered can attach the wrong state to the wrong item — the same class of bug a missing React key causes. Add (item.id) to any list that changes.
SvelteVueAngularReact
{#if} / {:else if} / {:else}v-if / v-else-if / v-else@if / @else ifternary / &&
{#each x as item (id)}v-for + :key@for + track.map() + key
{#each ...}{:else}separate check@emptyseparate check
{#await} / {:then} / {:catch}— (or React Query/Suspense)

Coding Challenges

Challenge 1

Build a component with a status $state ("loading"/"error"/"success") cycled by a button, using {#if}/{:else if}/{:else} to show different content per status.

📄 View solution
Challenge 2

Given a $state array of objects ({ id, name }), render them with {#each} (keyed on id) in a numbered list using the index, and include a {:else} branch showing "No items" when the array is cleared by a button.

📄 View solution
Challenge 3

Build a component that fetches from any free public API into a promise and renders it with {#await}/{:then}/{:catch} — showing a loading message, the result on success, and an error message on failure, with no manual status state.

📄 View solution

Chapter 5 Quick Reference

  • {#if} / {:else if} / {:else} / {/if} — conditional rendering; # opens, : continues, / closes
  • {#each items as item (id)} — list rendering; the (id) key is optional but advised for dynamic lists
  • {#each items as item, index} — also exposes the index
  • {#each ...}{:else} — built-in empty-list fallback (= Angular's @empty)
  • {#await promise}{:then data}{:catch error} — render a Promise's three states inline (unique to Svelte)
  • Key any list that gets reordered/filtered, the same discipline as React's key
  • Next chapter: components and props ($props)