Components
Every .svelte file is a component. Components have three sections: script, markup, and style.
<script>
// JavaScript logic
let name = $state("world");
</script>
<h1>Hello {name}!</h1>
<style>
/* Scoped to this component only */
h1 { color: purple; }
</style>
Props
Pass data to child components with $props():
<!-- Child.svelte -->
<script>
let { name, greeting = "Hello" } = $props();
</script>
<p>{greeting}, {name}!</p>
<!-- Parent.svelte -->
<script>
import Child from './Child.svelte';
</script>
<Child name="Alice" />
<Child name="Bob" greeting="Hey" />
Snippets (replacing slots)
Svelte 5 uses snippets instead of slots for passing content:
<!-- Card.svelte -->
<script>
let { header, children } = $props();
</script>
<div class="card">
<div class="header">{@render header()}</div>
<div class="body">{@render children()}</div>
</div>
<!-- Usage -->
<Card>
{#snippet header()}
<h2>Title</h2>
{/snippet}
<p>Card content goes here.</p>
</Card>
Conditional Rendering
{#if condition}
<p>Truthy</p>
{:else if other}
<p>Other</p>
{:else}
<p>Falsy</p>
{/if}
Lists
{#each items as item, index (item.id)}
<li>{index}: {item.name}</li>
{/each}
Tip: Always provide a key expression
(item.id)for lists that can change. Without it, Svelte updates items by index which causes bugs with stateful components.
Scoped Styles
CSS in <style> only affects the current component:
<style>
p { color: blue; } /* Only <p> tags in THIS component */
:global(body) { margin: 0; } /* Escapes scoping */
</style>