Learn Svelte - Component Communication
Episode 5 of 24

Learn Svelte - Component Communication

This episode covers how Svelte components communicate: props for data from parent to child, event dispatch for child to parent, slots for content injection, two-way binding with bind, and patterns for reusable, maintainable component composition.

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

Introduction

Real applications are never a single giant component. You break the view into small components, each with a single responsibility, then assemble them like LEGO blocks. What determines the quality of your architecture is not component size, but how components talk to each other.

Svelte offers three clear communication channels: props flow downward from parent to child, event dispatch flows upward from child to parent, and slots inject content from outside. Combined with bind: for two-way binding, you can build a tidy component hierarchy without messy global state.

This episode covers all four mechanisms, complete with real examples. You will also learn composition patterns that make components genuinely reusable — not just for this project, but for your whole career as a Svelte developer.

Props: Data from Parent to Child

Declaring Props with $props

In Svelte 5, props are declared with the $props rune. The child receives values from the parent through attributes:

Child component with props
<script>
  let { nama, umur } = $props()
</script>
 
<p>{nama} berumur {umur} tahun</p>

let { nama, umur } = $props() marks nama and umur as props the parent can send. In Svelte 4, the syntax was export let nama. Both are valid, but $props gives better TypeScript support.

Sending Props from the Parent

The parent uses the child component with attributes matching the prop names:

Parent sending props
<script>
  import Pengguna from "./Pengguna.svelte"
</script>
 
<Pengguna nama="Arman" umur={28} />

nama="Arman" sends a string literal, while umur={28} sends an expression. Props help separate data from presentation: the Pengguna component does not care where the data comes from; it simply receives and renders it.

Event Dispatch: Child to Parent

createEventDispatcher

To send a message to the parent, the child uses createEventDispatcher from svelte:

Child dispatching an event
<script>
  import { createEventDispatcher } from "svelte"
 
  const dispatch = createEventDispatcher()
 
  function pilih() {
    dispatch("pilih", { id: 42 })
  }
</script>
 
<button onclick={pilih}>Pilih item</button>

dispatch("pilih", { id: 42 }) sends an event named pilih with an object payload. The parent catches this event without needing to share state — the child stays independent.

Catching the Event in the Parent

The parent listens for events with an attribute matching the event name:

Parent catching the event
<script>
  import Item from "./Item.svelte"
 
  function handlePilih(event) {
    console.log("Dipilih:", event.detail.id)
  }
</script>
 
<Item onpilih={handlePilih} />

onpilih={handlePilih} listens for the pilih event. The payload sent by dispatch is available in event.detail. This pattern keeps data flowing in one direction: the parent provides data through props, the child reports back through events.

Slots: Injecting Content

Basic Slots

Slots let the parent place markup inside the child component:

Component with a slot
<div class="kartu">
  <slot />
</div>

<slot /> is the point where the parent's content is rendered. The component becomes a refillable container — the pattern used by libraries for modals, accordions, and cards. Without slots, you would be forced to write many props for every small piece of the view.

Named Slots and Slot Props

For several content areas, use named slots. Slots can also send data back to the parent:

Named slot with slot props
<div class="kartu">
  <header><slot name="judul">Judul default</slot></header>
  <main><slot konten={{ item: "data" }} /></main>
</div>

<slot name="judul"> defines a named slot that the parent fills with the slot="judul" attribute. Slot props like konten={{ item: "data" }} send data from the child to the parent — the reverse of the usual direction, useful for flexible layouts.

Two-Way Binding with bind

Connecting State Between Components

The property binding you learned in episode 4 can be used between components with bind:prop syntax:

Child with a bindable prop
<script>
  let { nilai } = $props()
</script>
 
<input bind:value={nilai} />
Parent using bind
<script>
  import SliderNilai from "./SliderNilai.svelte"
 
  let nilai = $state(50)
</script>
 
<SliderNilai bind:nilai />

bind:nilai makes the nilai prop two-way: changes in the child input update nilai in the parent, and vice versa. This is an ergonomic alternative to the dispatch pattern when parent and child genuinely share a single value.

Use bind: when the state really is one with the child — inputs, sliders, toggles. Use event dispatch when the child merely reports an occurrence, while the decision and state stay in the parent. The principle: do not bind anything that could be passed one-way.

Reusable Component Composition

Container and Presentational Pattern

One of the most effective patterns: presentational components only render props, while container components manage state and logic. Here is an example combining all the mechanisms:

Full composition
<script>
  import Tombol from "./Tombol.svelte"
 
  let klik = $state(0)
</script>
 
<Tombol onklik={() => (klik += 1)}>
  Klik saya {klik} kali
</Tombol>

The Tombol component receives a slot for its label and dispatches a klik event. The parent manages the klik state. The result: Tombol can be used anywhere without knowing its context, while the parent keeps full control over the data.

Principles of Good Composition

A few guidelines that keep components clean:

  • One component, one responsibility — separate presentation from business logic.
  • Props for incoming data, events for outgoing occurrences, slots for flexible content.
  • Avoid too many props; if there are more than five, consider splitting the component.

Conclusion

Key takeaways:

  • Props with $props send data from parent to child, complete with defaults.
  • createEventDispatcher and dispatch send events from child to parent.
  • Slots let the parent inject markup; named slots for multiple areas.
  • bind:prop creates two-way communication between components sharing a value.
  • Separate presentational components from the containers that hold logic.
  • Choose bind for shared values and events for occurrence notifications.

In the next episode 6 we will discuss stores and state management — writable, readable, and derived stores, custom stores, subscription, and state management patterns for small and medium applications. The component communication you mastered today will complement the stores that are the main theme of the next episode.

Learn Svelte - Component Communication | Learn Svelte