Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Nested content and snippets

Sometimes a component needs not data but a whole piece of markup: a card with arbitrary content, a list where the parent decides what a row looks like. In Svelte 5 such a piece of markup is called a snippet, and it is rendered with the {@render} tag.

Content between the tags

Whatever the parent writes between a component's opening and closing tags arrives in the children prop. Create the file src/lib/components/Card.svelte:

<!-- src/lib/components/Card.svelte -->
<script>
  let { children } = $props();
</script>
 
<section class="card">
  {@render children()}
</section>
 
<style>
  .card {
    border: 1px solid #ddd;
    border-radius: 8px;
    padding: 16px;
  }
</style>

The component draws a frame and renders whatever it was given inside.

Using it

<!-- src/routes/+page.svelte -->
<script>
  import Card from '$lib/components/Card.svelte';
</script>
 
<Card>
  <h2>My tasks</h2>
  <p>Tasks for today: 3</p>
</Card>

The page shows a frame with rounded corners containing the heading and the paragraph. One Card component fits any content.

When there is no content

If you write <Card /> with no content, children is undefined, and children() throws an error. For optional content, call it with ?.:

<!-- src/lib/components/Card.svelte -->
<script>
  let { children } = $props();
</script>
 
<section class="card">
  {@render children?.()}
</section>

Your own snippet with a parameter

A snippet can also be declared by hand with a {#snippet name(params)} block. Declared inside a component's tag, it becomes a prop with the same name. Let us make a list where the parent decides what a row looks like:

<!-- src/lib/components/TodoList.svelte -->
<script>
  let { todos, row } = $props();
</script>
 
<ul>
  {#each todos as todo (todo.id)}
    <li>{@render row(todo)}</li>
  {/each}
</ul>
<!-- src/routes/+page.svelte -->
<script>
  import TodoList from '$lib/components/TodoList.svelte';
 
  let todos = $state([
    { id: 1, text: 'Buy bread', done: true },
    { id: 2, text: 'Call Oleg', done: false }
  ]);
</script>
 
<TodoList {todos}>
  {#snippet row(todo)}
    <strong>{todo.done ? '✓' : '○'}</strong> {todo.text}
  {/snippet}
</TodoList>

TodoList goes through the tasks and calls the row snippet for each, passing it the task. The look of the row is decided by the page: a bold mark and the text.

A snippet inside one component

A snippet declared in regular markup can be rendered several times in the same file — a way to avoid repeating the same piece of markup without creating a separate component.

Common mistake

Rendering a snippet with curly braces and no @render: {children} or {row(todo)}. A snippet is a special kind of function, not a string, so nothing useful appears on the page. Snippets are always rendered with the {@render ...} tag.

Thanks for reading!

Donate to the site — we work hard for you.