08 - Control Flow
📋 Jump to Takeaways🎁 You have reactive state, but how do you actually show a list, hide something when a user logs out, or display a spinner while data loads? None of that needs a rune.
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, and 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. It 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.tsload 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
$statewhen 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 the DOM entirely{#each}: loops, use(key)for dynamic lists to avoid bugs{#each ... {:else}}: handles empty arrays{#await}: loading, success, and error states for promises (prefer SvelteKit load functions for page data){#key}: destroy and recreate content when a value changes- These are template blocks, not runes, and use the same syntax since Svelte 3/4
🎁 What if a value keeps changing and you have no idea why, or you want to log every update during development without shipping that noise to production? Svelte has two specialized runes for exactly those moments.