React

Svelte 5 snippets and slots: reusable content without components

In Svelte 5 a snippet is named markup: declared with {#snippet}, run with {@render}, it replaces slots while keeping the parent scope.

Key points

  • A snippet is declared with {#snippet name(...)} and run with {@render name(...)}.
  • Content between a component's tags arrives as the implicit children snippet: a prop, not a slot.
  • A snippet is not a component: it has no own state, no lifecycle and no import from a .js file.

In Svelte 5 a snippet is markup with a name: you declare it with {#snippet}, run it with {@render}, and it takes the place of slots without going through a component.

A snippet is a markup function

The declaration takes parameters and the render receives them. A snippet stays in the scope where it is written, so it sees the component variables without taking them as props.

npx sv create snippets-demo
{#snippet card(title, body)}
  
    {title}
    {body}
  
{/snippet}

{@render card("Invoice", "Due on 12 May")}

To hand a snippet to a child you declare it as a prop, typed Snippet from svelte: it is a function, so you pass it as a function and not as a string.


  import type { Snippet } from "svelte";

  let { header, children }: { header: Snippet; children: Snippet } = $props();


{@render header()}
{@render children()}

children replaces the default slot

Content written between a component's tags arrives as the implicit children snippet. When the parent passes nothing the child finds no value, and the optional render {@render children?.()} avoids the error.

What changes from slots

Svelte 4Svelte 5Difference
slotchildren and @renderThe content is a prop, not an element to fill
named slotsnippet as a propNo slot attribute in the parent
$slotschildren is presentThe check is on the prop value
slot propssnippet parametersThe child calls the parent markup with its data

The limitation to plan for

A snippet is not a component: it has no own state, no lifecycle, and you cannot import it from a .js file. It lives in the markup that declares it, so whoever receives it cannot subscribe to its changes without re-rendering.

Mistakes you will see

  • Writing {@render card} without parentheses: the render wants the call, not the function.
  • Looking for slot="header" in the parent: with snippets the name is a prop, not an attribute.
  • Declaring the snippet inside an {#if} and using it outside: the declaration only holds in that block.
  • Passing a snippet to a component that still uses <slot />: the two syntaxes do not talk.

The Svelte components in the catalogue already use this form: flowbite-svelte and svelte-bits install as source, and the faq block shows the case where a snippet is enough. Runes are in Svelte 5 runes, route files in routing and layouts in SvelteKit, the comparison in Svelte vs React.

Frequently asked questions

Are slots deprecated?

No, but they do not coexist with runes in the same component. A component written with runes uses snippets; the slot syntax stays for files in legacy mode.

Can I use one snippet in two components?

Yes, by passing it as a prop typed Snippet. You cannot import it from another file: the declaration lives in markup, so the owning component declares it and hands it over.

How do I pass data from the child to the snippet?

Through parameters: the child calls {@render row(item)} and each entry reaches the parent markup. It is the same job as slot props, without the let attribute.

Why does the content not appear?

Because nothing runs it: declaring a snippet prints nothing. You need a {@render name()} call in the same scope, or a pass to the child that makes the call.

Does it work with server rendering?

Yes. Snippets compile to functions and run during server rendering as they do in the browser, so the initial HTML carries the same markup.