iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

{#snippet}

Svelte 5 snippets are reusable template fragments you can pass between components — replacing slots with a more powerful, typed model. Use for layouts, repeated patterns, configurable cells.

Define, render, pass as props

EXAMPLE
<!-- 1) Define a snippet — declares a render function -->
<script>
    let items = [
        { id: 1, name: 'Ada', email: 'a@x.com' },
        { id: 2, name: 'Bo',  email: 'b@x.com' },
    ];
</script>

{#snippet user(u)}
    <div class="user">
        <h3>{u.name}</h3>
        <p>{u.email}</p>
    </div>
{/snippet}

<!-- 2) Render a snippet with {@render} -->
{@render user(items[0])}

<!-- 3) Use in a loop -->
{#each items as item (item.id)}
    {@render user(item)}
{/each}

<!-- 4) Multiple parameters -->
{#snippet card(title, body, footer)}
    <article>
        <h2>{title}</h2>
        <p>{body}</p>
        <footer>{footer}</footer>
    </article>
{/snippet}

{@render card('Title', 'Some body text', 'Footer text')}

<!-- 5) Conditional rendering of snippet -->
{#if hasContent}
    {@render content()}
{:else}
    <p>No content</p>
{/if}

<!-- 6) Pass snippets as PROPS to child components -->
<!-- Parent.svelte -->
<script>
    import Layout from './Layout.svelte';
</script>

{#snippet header()}
    <nav>Site navigation</nav>
{/snippet}

{#snippet footer()}
    <p>© 2026</p>
{/snippet}

<Layout {header} {footer}>
    <main>Page content</main>
</Layout>

<!-- Layout.svelte -->
<script>
    let { header, footer, children } = $props();
</script>

<header>
    {#if header}{@render header()}{/if}
</header>
<main>
    {@render children?.()}
</main>
<footer>
    {#if footer}{@render footer()}{/if}
</footer>

<!-- 7) Children is implicitly a snippet -->
<!-- Card.svelte -->
<script>
    let { children } = $props();
</script>

<div class="card">
    {@render children()}
</div>

<!-- Parent -->
<Card>
    <h2>Hello</h2>
    <p>Content inside</p>
</Card>

<!-- 8) Snippets with typed props (TS) -->
<!-- DataTable.svelte -->
<script lang="ts">
    import type { Snippet } from 'svelte';

    interface Props<T> {
        rows: T[];
        renderRow: Snippet<[T, number]>;
        renderHeader?: Snippet<[]>;
    }

    let { rows, renderRow, renderHeader }: Props<unknown> = $props();
</script>

<table>
    {#if renderHeader}
        <thead>{@render renderHeader()}</thead>
    {/if}
    <tbody>
        {#each rows as row, i (row.id)}
            <tr>{@render renderRow(row, i)}</tr>
        {/each}
    </tbody>
</table>

<!-- Parent.svelte -->
<DataTable rows={users}>
    {#snippet renderHeader()}
        <tr><th>Name</th><th>Email</th><th>Role</th></tr>
    {/snippet}

    {#snippet renderRow(user, i)}
        <td>{i + 1}. {user.name}</td>
        <td>{user.email}</td>
        <td>
            <span class="badge">{user.role}</span>
        </td>
    {/snippet}
</DataTable>

<!-- 9) Reusing snippets — export from a module -->
<!-- snippets.svelte.js — DOES NOT EXIST; snippets are template-only -->
<!-- Instead, put snippets in a component and reuse: -->

<!-- shared/UserBadge.svelte -->
<script>
    let { user } = $props();
</script>

<span class="badge">
    <img src={user.avatar} alt="" />
    {user.name}
</span>

<!-- 10) Default content with snippets -->
<!-- Button.svelte -->
<script>
    let { children, icon } = $props();
</script>

<button class="btn">
    {#if icon}
        {@render icon()}
    {/if}
    {@render children?.()}
</button>

<!-- Parent -->
<Button>Save</Button>                            <!-- no icon -->
<Button>
    {#snippet icon()}
        <svg>...</svg>
    {/snippet}
    Save
</Button>

<!-- 11) Computed snippet selection -->
{#snippet smallText(text)}
    <small>{text}</small>
{/snippet}

{#snippet bigText(text)}
    <h2>{text}</h2>
{/snippet}

<!-- Conditionally pick a snippet -->
{@const fn = level === 'big' ? bigText : smallText}
{@render fn('Hello')}

<!-- 12) Snippets vs slots — Svelte 5 changes -->
<!--
  Slots (Svelte 4 — still supported for compat):
    <slot name="header" />
    <slot {data} />                                  <!-- scoped slot -->

  Snippets (Svelte 5 — preferred):
    let { header, children } = $props();
    {@render header?.()}
    {@render children?.(data)}

  Snippets are more powerful:
    - Can pass parameters cleanly
    - Fully typed in TypeScript
    - Can be passed around as values
    - Cleaner conditional rendering
-->

<!-- 13) Real-world example — list with custom row -->
<!-- VirtualList.svelte -->
<script lang="ts">
    import type { Snippet } from 'svelte';
    interface Props<T> {
        items: T[];
        itemHeight?: number;
        children: Snippet<[T, number]>;
    }
    let { items, itemHeight = 40, children }: Props<unknown> = $props();
</script>

<div class="virtual-list" style:height={items.length * itemHeight + 'px'}>
    {#each items as item, i (item.id)}
        <div class="row" style:height={itemHeight + 'px'}>
            {@render children(item, i)}
        </div>
    {/each}
</div>

<!-- Parent -->
<VirtualList items={users}>
    {#snippet children(user, i)}
        <span>{i + 1}. {user.name}</span>
        <button onclick={() => editUser(user.id)}>Edit</button>
    {/snippet}
</VirtualList>

<!-- 14) Loading states with snippet variants -->
<AsyncData url="/api/posts">
    {#snippet loading()}
        <Spinner />
    {/snippet}

    {#snippet error(err)}
        <p class="error">{err.message}</p>
    {/snippet}

    {#snippet content(data)}
        <PostList posts={data} />
    {/snippet}
</AsyncData>

<!-- AsyncData.svelte -->
<script>
    let { url, loading, error: errorSnip, content } = $props();
    let data = $state(null);
    let err  = $state(null);

    $effect(() => {
        let active = true;
        fetch(url)
            .then(r => r.json())
            .then(d => { if (active) data = d; })
            .catch(e => { if (active) err = e; });
        return () => { active = false; };
    });
</script>

{#if err && errorSnip}
    {@render errorSnip(err)}
{:else if !data && loading}
    {@render loading()}
{:else if data && content}
    {@render content(data)}
{/if}

<!-- 15) Modifier / decorator pattern -->
<!-- Modal.svelte -->
<script>
    let { title, actions, children } = $props();
</script>

<dialog>
    <h2>{title}</h2>
    <div class="body">{@render children()}</div>
    {#if actions}
        <footer>{@render actions()}</footer>
    {/if}
</dialog>

<!-- Parent -->
<Modal title="Confirm">
    <p>Are you sure?</p>
    {#snippet actions()}
        <button onclick={cancel}>Cancel</button>
        <button onclick={confirm} class="danger">Delete</button>
    {/snippet}
</Modal>

<!-- 16) Common bugs -->
<!--   • Calling a snippet without {@render} → renders nothing -->
<!--   • Naming conflict: snippet 'children' overrides the implicit children prop -->
<!--   • Passing a snippet to a component expecting different shape — TS catches with Snippet<[Args]> -->
<!--   • Forgetting nullable check (?.) when snippet might not be passed -->

<!-- 17) Best practices -->
<!--   ✅ Use snippets over slots in new Svelte 5 code -->
<!--   ✅ Type snippet params with Snippet<[Type1, Type2]> -->
<!--   ✅ Provide fallback content in the component -->
<!--   ✅ Pass snippets as props for layouts; use children for body -->
<!--   ✅ Use @const + ternary to pick snippets at render time -->
<!--   ✅ Keep snippets focused — one concept per snippet -->

Why it matters

Snippets replace slots in Svelte 5 — cleaner, fully typed, can be passed as values. Use for layouts (header / footer / children), configurable cells in tables, and loading-state variants.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
{#snippet card({ title, body })}
    <section><h2>{title}</h2><p>{body}</p></section>
{/snippet}
{@render card({ title: 'Hi', body: 'There' })}
Try it Yourself »

Discussion

Loading…