Snippets, and render tags, are a way to create reusable chunks of markup inside your components. Instead of writing duplicative code like this...
{#each images as image}
{#if image.href}
<a href={image.href}>
<figure>
<img
src={image.src}
alt={image.caption}
width={image.width}
height={image.height}
/>
<figcaption>{image.caption}</figcaption>
</figure>
</a>
{:else}
<figure>
<img
src={image.src}
alt={image.caption}
width={image.width}
height={image.height}
/>
<figcaption>{image.caption}</figcaption>
</figure>
{/if}
{/each}
...you can write this:
{#snippet figure(image)}
<figure>
<img
src={image.src}
alt={image.caption}
width={image.width}
height={image.height}
/>
<figcaption>{image.caption}</figcaption>
</figure>
{/snippet}
{#each images as image}
{#if image.href}
<a href={image.href}>
{@render figure(image)}
</a>
{:else}
{@render figure(image)}
{/if}
{/each}
A snippet behaves pretty much like a regular function declaration: It can have multiple parameters, those parameters can be destructured, and they can have default values. However, you cannot use ...rest
params. (demo):
{#snippet figure({ src, caption, width, height })}
<figure>
<img alt={caption} {src} {width} {height} />
<figcaption>{caption}</figcaption>
</figure>
{/snippet}
Snippet scopepermalink
Snippets can be declared anywhere inside your component. They can reference values declared outside themselves, for example in the <script>
tag or in {#each ...}
blocks (demo)...
<script>
let { message = `it's great to see you!` } = $props();
</script>
{#snippet hello(name)}
<p>hello {name}! {message}!</p>
{/snippet}
{@render hello('alice')}
{@render hello('bob')}
...and they are 'visible' to everything in the same lexical scope (i.e. siblings, and children of those siblings):
<div>
{#snippet x()}
{#snippet y()}...{/snippet}
<!-- this is fine -->
{@render y()}
{/snippet}
<!-- this will error, as `y` is not in scope -->
{@render y()}
</div>
<!-- this will also error, as `x` is not in scope -->
{@render x()}
Snippets can reference themselves and each other (demo):
{#snippet blastoff()}
<span>🚀</span>
{/snippet}
{#snippet countdown(n)}
{#if n > 0}
<span>{n}...</span>
{@render countdown(n - 1)}
{:else}
{@render blastoff()}
{/if}
{/snippet}
{@render countdown(10)}
Passing snippets to componentspermalink
Within the template, snippets are values just like any other. As such, they can be passed to components as props (demo):
<script>
import Table from './Table.svelte';
const fruits = [
{ name: 'apples', qty: 5, price: 2 },
{ name: 'bananas', qty: 10, price: 1 },
{ name: 'cherries', qty: 20, price: 0.5 }
];
</script>
{#snippet header()}
<th>fruit</th>
<th>qty</th>
<th>price</th>
<th>total</th>
{/snippet}
{#snippet row(d)}
<td>{d.name}</td>
<td>{d.qty}</td>
<td>{d.price}</td>
<td>{d.qty * d.price}</td>
{/snippet}
<Table data={fruits} {header} {row} />
As an authoring convenience, snippets declared directly inside a component implicitly become props on the component (demo):
<!-- this is semantically the same as the above -->
<Table data={fruits}>
{#snippet header()}
<th>fruit</th>
<th>qty</th>
<th>price</th>
<th>total</th>
{/snippet}
{#snippet row(d)}
<td>{d.name}</td>
<td>{d.qty}</td>
<td>{d.price}</td>
<td>{d.qty * d.price}</td>
{/snippet}
</Table>
Any content inside the component tags that is not a snippet declaration implicitly becomes part of the children
snippet (demo):
<Table data={fruits}>
{#snippet header()}
<th>fruit</th>
<th>qty</th>
<th>price</th>
<th>total</th>
{/snippet}
<th>fruit</th>
<th>qty</th>
<th>price</th>
<th>total</th>
<!-- ... -->
</Table>
<script>
let { data, header, row } = $props();
let { data, children, row } = $props();
</script>
<table>
{#if header}
{#if children}
<thead>
<tr>{@render header()}</tr>
<tr>{@render children()}</tr>
</thead>
{/if}
<!-- ... -->
</table>
Note that you cannot have a prop called
children
if you also have content inside the component — for this reason, you should avoid having props with that name
Snippets and slotspermalink
In Svelte 4, content can be passed to components using slots. Snippets are more powerful and flexible, and as such slots are deprecated in Svelte 5.
They continue to work, however, and you can mix and match snippets and slots in your components.
Typing snippetspermalink
You can import the Snippet
type from 'svelte'
:
<script lang="ts">
import type { Snippet } from 'svelte';
let { header } = $props<{ header: Snippet }>();
</script>
The Snippet
type is generic. Here's how you'd type various cases:
ts
import type {Snippet } from 'svelte';typeSnippetWithNoArgs =Snippet ;typeSnippetWithOneArg =Snippet <[argOne : number]>;typeSnippetWithMultipleArgs =Snippet <[argOne : number,argTwo : string]>;
And here are the snippet declarations matching those cases (note: this example uses TypeScript):
{#snippet withNoArgs()}
<!-- -->
{/snippet}
{#snippet withOneArg(argOne: number)}
<!-- -->
{/snippet}
{#snippet withMultipleArgs(argOne: number, argTwo: string)}
<!-- -->
{/snippet}