08 - Control Flow

Svelte uses template blocks for conditional rendering, loops, async data, and re-rendering. These are NOT runes — they're part of Svelte's template syntax and work the same in Svelte 4 and 5.


{#if} — Conditional Rendering

Show or hide elements based on a condition.

<!-- src/lib/components/UserStatus.svelte -->
<script lang="ts">
  let { loggedIn, isAdmin } = $props();
</script>

{#if loggedIn && isAdmin}
  <p>Welcome, admin!</p>
{:else if loggedIn}
  <p>Welcome back!</p>
{:else}
  <p>Please log in.</p>
{/if}
  • {:else if} and {:else} are optional
  • The block completely removes elements from the DOM (not just hiding with CSS)

{#each} — Loops

Render a list of items.

Basic Loop

<!-- src/lib/components/CourseList.svelte -->
<script lang="ts">
  let courses = $state(['Svelte', 'TypeScript', 'SvelteKit']);
</script>

<ul>
  {#each courses as course}
    <li>{course}</li>
  {/each}
</ul>

With Index

{#each courses as course, index}
  <li>{index + 1}. {course}</li>
{/each}

With Key (important for dynamic lists)

When items can be added, removed, or reordered, Svelte needs a unique key to track which DOM element belongs to which item. Without a key, Svelte updates by position — which causes bugs with animations, component state, and reordering.

<script lang="ts">
  type Todo = { id: number; text: string };
  let todos = $state<Todo[]>([
    { id: 1, text: 'Learn Svelte' },
    { id: 2, text: 'Build LMS' }
  ]);
</script>

<!-- (item.id) is the key — must be unique per item -->
{#each todos as todo (todo.id)}
  <li>{todo.text}</li>
{/each}

When to use a key:

  • List items can be reordered, added, or removed → always use a key
  • Static list that never changes → key is optional

Destructuring

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

Empty List with {:else}

{#each todos as todo (todo.id)}
  <li>{todo.text}</li>
{:else}
  <p>No items yet.</p>
{/each}

The {:else} block renders when the array is empty.


{#await} — Async Data

Handle promises directly in the template — shows loading, success, and error states.

Full Pattern (loading → success → error)

<!-- src/lib/components/CourseLoader.svelte -->
<script lang="ts">
  type Course = { title: string; lessons: number };

  async function fetchCourse(): Promise<Course> {
    const res = await fetch('/api/course');
    if (!res.ok) throw new Error('Failed to load');
    return res.json();
  }

  let coursePromise = fetchCourse();
</script>

{#await coursePromise}
  <p>Loading course...</p>
{:then course}
  <h2>{course.title}</h2>
  <p>{course.lessons} lessons</p>
{:catch error}
  <p>Error: {error.message}</p>
{/await}

Skip Loading State

If you don't need a loading indicator:

{#await coursePromise then course}
  <h2>{course.title}</h2>
{/await}

Skip Error Handling

{#await coursePromise}
  <p>Loading...</p>
{:then course}
  <h2>{course.title}</h2>
{/await}

Note: In SvelteKit, you'll usually load data in +page.ts load functions instead of using {#await} in templates. {#await} is more useful for client-side fetches triggered by user actions.


{#key} — Force Re-render

Destroys and recreates its contents when the expression changes. Useful for resetting component state or re-triggering transitions.

<!-- src/lib/components/LessonView.svelte -->
<script lang="ts">
  let { lessonId } = $props();
</script>

<!-- When lessonId changes, the entire block is destroyed and recreated -->
{#key lessonId}
  <LessonContent id={lessonId} />
{/key}

Without {#key}, Svelte would reuse the same component instance and just update props. With {#key}, it creates a fresh instance — resetting all internal state.

When to use:

  • Reset a component's internal $state when a prop changes
  • Re-trigger intro/outro animations on data change
  • Force a fresh fetch inside a component

Key Takeaways

  • {#if} — conditional rendering, removes elements from DOM entirely
  • {#each} — loops, use (key) for dynamic lists to avoid bugs
  • {#each ... {:else}} — handles empty arrays
  • {#await} — loading/success/error for promises (prefer SvelteKit load functions for page data)
  • {#key} — destroy and recreate content when a value changes
  • These are template blocks, not runes — same syntax since Svelte 3/4