Svelte 5 snippets vs slots
- Last updated
- Prerequisites:
- Svelte component basics
- Experience with named/default slots in Svelte 4
- svelte
- snippets
- slots
- composition
- svelte5
- templates
Read at your depth
The practical view
Slots let a parent project content into a child: <Card><p>hello</p></Card> fills Card's default <slot />. Snippets change the model: the child declares {@render header()} where it wants content, and the parent defines {#snippet header()}...{/snippet} and passes it as header. Snippets are first-class values: you can pass multiple named ones, reuse one snippet in several places inside the child, and even pass a snippet to a snippet ({@render} with args — parameterized content like {#snippet row(item)}). With snippets you can pass snippets from one component to another as props, which slots could not do cleanly.
The same idea in other frameworks
// React: children props and render props
<Card header={(h) => <h2>{h}</h2>}>…</Card>// Angular: <ng-content> for slots; <ng-template> + ngTemplateOutlet
// for passable content fragments// Vue: <slot> for projection; scoped slots via
// <template #default="{ item }"> for parameterized content{#snippet header()}<h2>Title</h2>{/snippet}
<Card {header} />Legacy vs modern
Named slots vs snippets with parameters
Slots only pass markup into fixed holes; snippets are functions that can take arguments, be reused, and be forwarded — enabling parameterized row rendering.
<Card>
<svelte:fragment slot="footer">
<p>Done</p>
</svelte:fragment>
</Card>{#snippet row(item)}<li><strong>{item.name}</strong></li>{/snippet}
<List {items} {row} />Interview gotchas
Context
This checks whether the candidate understands content projection as data flow, not just 'a nicer syntax for slots'.
The mechanical answer
Snippets are functions: they can take parameters (so a list can call the same snippet per item with real data), be rendered multiple times, be forwarded through wrapper components as ordinary props, and be typed. Slots were fixed, named holes that required a slot-proxy mechanism and special forwarding ($$slots, slot inheritance) to pass through wrappers. With snippets, a component's content contract becomes an explicit, typed prop list, which makes the API introspectable, testable, and safe to refactor. The design consequence is that libraries move from 'slot conventions' to 'documented snippet props', and composition such as higher-order layouts becomes plain function passing.
Trap
The naive answer is 'snippets are just named slots'. They are a different mechanism: slots are a runtime projection mechanism with a slot object; snippets are compiled-down function props called with {@render}. Also, candidates often claim snippets replaced slots entirely — Svelte 5 still supports <slot> for compatibility, but new code uses snippets, and the slot mechanism is legacy. Mentioning parameterized rendering ({#snippet row(item)}) as the genuinely new capability is the differentiator.
Context
This targets the compiled data-flow model: the interviewer wants to know what a snippet closes over and how the signal graph connects parent and child.
The mechanical answer
A snippet is a function declared in the parent component's scope; the compiler lowers it to a function that closes over the parent's reactive state (its $state sources) and DOM update instructions. When the parent passes that function to the child and the child renders it with {@render}, the execution context is still the parent's scope — reads of the parent's $state inside the snippet body register dependencies against those sources, and the DOM statements in the snippet re-run when those sources change, regardless of which component called {@render}. That is why a snippet's content updates with the parent's state even though it renders inside the child's tree: reactivity follows the function's closure, not the render location.
Trap
The trap answer is 'snippets are reactive because the child re-renders'. The child does not re-run the snippet; the snippet's own dependencies drive its updates. Another trap: claiming snippets are re-created on every parent render like inline arrow functions in React — the compiler hoists and caches the snippet function per component instance, so it is not a per-render allocation. Candidates who describe the closure-over-parent-scope model demonstrate the real understanding.