Props
Component props in Svelte 5 are declared via the \$props rune. Destructure with defaults; mark bindable for two-way binding; use rest for spread.
Props, defaults, bindable, spread
EXAMPLE
<!-- 1) Declare props with the $props rune -->
<script>
let { title, count = 0, items = [] } = $props();
</script>
<h2>{title}</h2>
<p>Count: {count}</p>
<ul>
{#each items as item}
<li>{item.name}</li>
{/each}
</ul>
<!-- Parent -->
<UserCard title="Hello" count={3} items={users} />
<!-- 2) TypeScript prop types -->
<script lang="ts">
interface Props {
title: string;
count?: number;
items: Item[];
onSelect?: (i: Item) => void;
}
let { title, count = 0, items, onSelect }: Props = $props();
</script>
<!-- 3) Rest props — capture extra attributes -->
<script>
let { class: className = '', ...rest } = $props();
</script>
<button class="btn ${className}" {...rest}>
<slot />
</button>
<!-- Pass id, aria-*, data-* etc through to the underlying element -->
<MyButton id="save" aria-label="Save" data-testid="btn-save">Save</MyButton>
<!-- 4) Bindable props — two-way binding -->
<!-- TextInput.svelte -->
<script>
let { value = $bindable('') } = $props();
</script>
<input bind:value />
<!-- Parent -->
<script>
let name = $state('');
</script>
<TextInput bind:value={name} />
<p>{name}</p>
<!-- 5) Children — the children of a component -->
<!-- Layout.svelte -->
<script>
let { children, footer } = $props();
</script>
<header>Site header</header>
<main>
{@render children?.()}
</main>
<footer>
{#if footer}{@render footer()}{/if}
</footer>
<!-- Parent -->
<Layout>
{#snippet footer()}
<p>© 2026</p>
{/snippet}
<article>Body content</article>
</Layout>
<!-- 6) Default values for falsy props -->
<script>
let { user = { name: 'Anonymous', avatar: '/default.png' } } = $props();
</script>
<!-- 7) Reactive props — they auto-update when the parent changes them -->
<script>
let { count } = $props();
let double = $derived(count * 2); // updates when parent changes count
$effect(() => console.log('count changed to', count));
</script>
<!-- 8) Validate props at runtime (no formal API; use a custom check) -->
<script>
let { rating } = $props();
$effect(() => {
if (typeof rating !== 'number' || rating < 0 || rating > 5) {
console.warn('Invalid rating:', rating);
}
});
</script>
<!-- 9) Slots — children + named slots (Svelte 4 compat) -->
<!-- Card.svelte (Svelte 4 syntax — still works in 5) -->
<div class="card">
<header>
<slot name="title">Default title</slot>
</header>
<div class="body">
<slot>Default body</slot>
</div>
<footer>
<slot name="actions" />
</footer>
</div>
<!-- Parent -->
<Card>
<h2 slot="title">My title</h2>
<p>Body content</p>
<button slot="actions">OK</button>
</Card>
<!-- Svelte 5 prefers snippets over slots for new code (more powerful + typed). -->
<!-- 10) Forwarding events -->
<!-- Svelte 5: event handlers are just props named onclick / onsubmit / etc. -->
<!-- Parent passes a handler; child invokes it -->
<!-- Child -->
<script>
let { onclick } = $props();
</script>
<button {onclick}>Click</button>
<!-- Parent -->
<MyButton onclick={() => count++} />
<!-- 11) Common patterns -->
<!-- Polymorphic component — render as a different tag -->
<script>
let { as = 'div', children, ...rest } = $props();
let Tag = $derived(as);
</script>
<svelte:element this={Tag} {...rest}>
{@render children()}
</svelte:element>
<!-- Conditional props -->
<MyInput type={isPassword ? 'password' : 'text'} />
<!-- 12) Common bugs -->
<!--
• Forgetting $bindable — bind:value on a non-bindable prop is an error
• Destructuring with defaults at the top of script BUT the rest object loses reactivity
→ always use { ...rest } after defaults
• Modifying props directly — they're reactive READ-ONLY; emit events / use $bindable
• Missing keys in {#each} → wrong child instances reused on reorder
-->
Why it matters
Svelte 5 props are runes-driven: \$props() for inputs, \$bindable for two-way, {@render children()} for slot equivalents. Cleaner and more typed than slots, with full Svelte reactivity.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
<!-- Child.svelte -->
<script>
let { name, age = 0 } = $props();
</script>
<h2>{name} ({age})</h2>
Try it Yourself »
Discussion
Loading…