Svelte
02 / 07

Component Communication

Svelte Component Communication

Learn how Svelte components communicate through props, events, slots, and context:

Props - Parent to Child

<!-- Child.svelte -->
<script>
  // Declare props with export
  export let title;
  export let count = 0;  // With default value
  export let user = null;  // Optional prop
  
  // Props are reactive
  $: console.log('Title changed:', title);
</script>

<div>
  <h2>{title}</h2>
  <p>Count: {count}</p>
  {#if user}
    <p>User: {user.name}</p>
  {/if}
</div>

<!-- Parent.svelte -->
<script>
  import Child from './Child.svelte';
  
  let parentTitle = 'Hello';
  let parentCount = 5;
  let currentUser = { name: 'John' };
</script>

<Child title={parentTitle} count={parentCount} user={currentUser} />

<!-- Shorthand when variable name matches prop name -->
<Child {title} {count} {user} />

Events - Child to Parent

<!-- Child.svelte -->
<script>
  import { createEventDispatcher } from 'svelte';
  
  const dispatch = createEventDispatcher();
  
  function sendMessage() {
    dispatch('message', {
      text: 'Hello from child'
    });
  }
  
  function increment() {
    dispatch('increment', { value: 1 });
  }
</script>

<button on:click={sendMessage}>Send Message</button>
<button on:click={increment}>Increment</button>

<!-- Parent.svelte -->
<script>
  import Child from './Child.svelte';
  
  let count = 0;
  let lastMessage = '';
  
  function handleMessage(event) {
    lastMessage = event.detail.text;
  }
  
  function handleIncrement(event) {
    count += event.detail.value;
  }
</script>

<Child on:message={handleMessage} on:increment={handleIncrement} />

<p>Count: {count}</p>
<p>Last message: {lastMessage}</p>

Two-Way Binding (bind:)

<!-- Child.svelte -->
<script>
  export let value;
</script>

<input type="text" bind:value />

<!-- Parent.svelte -->
<script>
  import Child from './Child.svelte';
  let searchQuery = '';
</script>

<!-- Two-way binding -->
<Child bind:value={searchQuery} />
<p>You typed: {searchQuery}</p>

Slots - Content Projection

<!-- Card.svelte -->
<div class="card">
  <header>
    <slot name="header">Default Header</slot>
  </header>
  
  <main>
    <slot>Default content</slot>
  </main>
  
  <footer>
    <slot name="footer" />
  </footer>
</div>

<!-- Usage -->
<script>
  import Card from './Card.svelte';
</script>

<Card>
  <h2 slot="header">Card Title</h2>
  
  <!-- Default slot -->
  <p>This is the main content.</p>
  
  <div slot="footer">
    <button>Cancel</button>
    <button>Save</button>
  </div>
</Card>

<!-- Slot props - pass data to parent -->
<!-- List.svelte -->
<script>
  export let items = [];
</script>

{#each items as item, index}
  <slot {item} {index}>
    <!-- Fallback content -->
    <p>{item.name}</p>
  </slot>
{/each}

<!-- Parent usage -->
<List {items} let:item let:index>
  <div>
    <strong>{index + 1}.</strong>
    <span>{item.name}</span>
    <span>${item.price}</span>
  </div>
</List>

Context API

<!-- Parent.svelte -->
<script>
  import { setContext } from 'svelte';
  import Child from './Child.svelte';
  
  const theme = {
    color: 'blue',
    background: 'white'
  };
  
  setContext('theme', theme);
  
  // Can also use writable store
  import { writable } from 'svelte/store';
  const user = writable({ name: 'John' });
  setContext('user', user);
</script>

<Child />

<!-- Child.svelte (any level deep) -->
<script>
  import { getContext } from 'svelte';
  
  const theme = getContext('theme');
  const user = getContext('user');
</script>

<div style="color: {theme.color}; background: {theme.background}">
  <p>User: {$user.name}</p>
</div>

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free