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>