Conditionals and lists appear on nearly every screen, and keys decide whether list items keep their state.
Svelte markup uses blocks for logic. They open with #, continue with : and close with /.
Conditionals:
{#if user}
<p>Hi {user.name}</p>
{:else if loading}
<p>Loading...</p>
{:else}
<button>Log in</button>
{/if}Lists with {#each}:
{#each items as item}or{#each items as item, i}for the index.- Destructure directly:
{#each users as { id, name }}. - An
{:else}branch renders when the list is empty. - Add a key in parentheses:
{#each todos as todo (todo.id)}. With a key, Svelte moves, inserts and removes DOM nodes to match items; without one it updates nodes in place by position, so state like input focus or a child component's internal state can end up on the wrong row after insertions or deletions. Keys are also required foranimate:directives.
{#each} accepts arrays, array-like objects and other iterables such as Sets (they are converted with Array.from).
`{#key value}` destroys and recreates its content whenever value changes. Use it to restart a transition or reset a child component's state when, say, the selected id changes.
{@html markup} renders raw HTML; only use it with trusted, sanitised content.
Example
<script>
let todos = $state([
{ id: 1, text: 'Buy milk', done: false },
{ id: 2, text: 'Learn Svelte', done: true },
]);
let selected = $state(1);
</script>
{#each todos as todo, i (todo.id)}
<label class:done={todo.done}>
{i + 1}. <input type="checkbox" bind:checked={todo.done} /> {todo.text}
</label>
{:else}
<p>Nothing to do!</p>
{/each}
{#if todos.every((t) => t.done)}
<p>All done</p>
{:else}
<p>{todos.filter((t) => !t.done).length} left</p>
{/if}
{#key selected}
<p>Details for #{selected}</p> <!-- recreated when selected changes -->
{/key}Remember
{#if}...{:else if}...{:else}...{/if}for conditionals{#each list as item, i (item.id)}with a stable key;{:else}for empty lists{#key x}recreates its content whenever x changes
Watch out for
- Leaving out the key in lists whose items are reordered or removed, so per-row state sticks to the wrong item.