Learn Svelte - Template & Bindings
Episode 4 of 24

Learn Svelte - Template & Bindings

This episode dissects the Svelte template language: variable declarations and interpolation, property binding, event binding, class binding, reactive statements, stores, and conditional and list rendering with #if and #each. These are the core syntaxes used throughout every following episode.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Episode 3 gave you a working project. Episode 4 fills that project with content: the Svelte template language. Every component of yours is a combination of markup, state, and logic — and this episode covers how the three connect through interpolation, bindings, and render directives.

One of Svelte's strengths is that you write views that look like plain HTML, yet reactivity is hidden inside. {nama} updates itself, bind:value links an input to state, and #each renders lists that keep up with changes. There is no abstract API to memorize — the syntax is JavaScript embedded in markup.

In this episode you will learn variable declarations and interpolation, property and event binding, reactive statements, stores, and conditional and list rendering. When you finish, you can build almost any interface view an application needs.

Variable Declarations and Template Syntax

State and Interpolation

All Svelte state is JavaScript variables in the <script> block. In Svelte 5 with runes, use $state for reactive variables:

Basic interpolation
<script>
  let pesan = $state("Halo Svelte")
  let jumlah = $state(3)
</script>
 
<h1>{pesan}</h1>
<p>Jumlah: {jumlah}</p>

let pesan = $state("Halo Svelte") declares reactive state. The curly braces {pesan} in markup are interpolation: the value is rendered as text and updates automatically when state changes. In Svelte 4, you would simply write let pesan = "Halo Svelte" without $state — both are valid, but runes are the future.

Property, Event, and Class Binding

Property Binding with bind

To connect form elements to state two-way, Svelte provides bind:. The state value changes as the user types, and the view updates when the state changes from code:

Two-way input binding
<script>
  let nama = $state("")
</script>
 
<input bind:value={nama} placeholder="Ketik nama" />
<p>Halo, {nama || "anonim"}!</p>

bind:value={nama} keeps nama always in sync with the input's content. This is two-way: typing updates the state, and changing the state updates the input. Other common bindings: bind:checked for checkboxes, bind:files for file inputs, and bind:this for direct DOM element references.

Event Binding with onclick

In Svelte 5, event handlers are written as element properties. Attach a function or expression directly:

Modern event handler
<script>
  let count = $state(0)
 
  function tambah() {
    count += 1
  }
</script>
 
<button onclick={tambah}>Klik {count}</button>
<button onclick={() => (count = 0)}>Reset</button>

onclick={tambah} binds the click event to the tambah function. In Svelte 4 the syntax was on:click={tambah} — still supported, but the property form is the new style. Other events like oninput, onsubmit, and onkeydown follow the same pattern.

Dynamic Class Binding

To toggle a class reactively, use an object or array inside class::

Conditional class binding
<script>
  let aktif = $state(false)
</script>
 
<button
  class="tombol"
  class:aktif
  class:mati={!aktif}
  onclick={() => (aktif = !aktif)}
>
  Status: {aktif ? "aktif" : "mati"}
</button>

class:aktif adds the aktif class when its variable value is truthy, and removes it when falsy. The class:mati={!aktif} form uses an expression. This is the most ergonomic way to style elements based on state.

Reactive Statements and Stores

Reactive Statements

Before runes, $: was the heart of Svelte reactivity. This syntax still works and is useful for derived values:

Reactive statement
<script>
  let harga = $state(1000)
  let pajak = $derived(harga * 0.11)
</script>
 
<p>Harga: {harga}</p>
<p>Pajak: {pajak}</p>
<p>Total: {harga + pajak}</p>

let pajak = $derived(harga * 0.11) recalculates pajak every time harga changes. This is equivalent to $: pajak = harga * 0.11 in Svelte 4. The difference: $derived is only for pure derived values, while $: can contain statements and side effects.

Getting to Know Stores

For state used by many components, use a store from svelte/store:

JSSimple writable store
import { writable } from "svelte/store"
 
export const keranjang = writable([])

writable([]) creates a store containing an empty array. Components read its value with the $ prefix — for example $keranjang — and modify it with .set() or .update(). The full store concept is covered in episode 6.

Conditional and List Rendering

Conditional Rendering with #if

To display a block of markup based on a condition, use #if for the first block and an else block for the second branch:

Conditional rendering
<script>
  let login = $state(false)
</script>
 
{#if login}
  <p>Selamat datang kembali!</p>
{:else}
  <button onclick={() => (login = true)}>Login</button>
{/if}

{#if login} opens the conditional block and {/if} closes it. Svelte removes unused blocks from the DOM — it does not merely hide them. That makes conditional rendering both efficient and easy to read.

List Rendering with #each

To render an array, use #each with an optional key for accurate list updates:

List rendering with key
<script>
  let tugas = $state(["Belajar", "Latihan", "Deploy"])
</script>
 
<ul>
  {#each tugas as t, i (t)}
    <li>{i + 1}. {t}</li>
  {/each}
</ul>

{#each tugas as t, i (t)} iterates the tugas array, providing the value t and the index i. The expression (t) is a key that helps Svelte track items as the array changes. Without a key, list reordering can produce incorrect DOM.

Conclusion

Key takeaways:

  • Use $state for reactive variables and {expression} for interpolation in markup.
  • bind: makes form properties two-way; onclick binds events in Svelte 5.
  • class:aktif adds or removes a class based on a condition.
  • $derived and $effect replace the majority of $: statements in Svelte 5.
  • #if and #each handle conditional and list rendering.
  • Always give #each a key so list updates stay consistent.

In the next episode 5 we will discuss component communication — how components exchange data through props, slots, and event dispatch, including two-way binding with bind: and patterns for reusable component composition. The template syntax from this episode will be your main toolkit.