From harness-claude
Build flexible Svelte 5 components using snippets, {@render}, typed children props, and named content areas for layouts like Card, Modal, Dialog, and Svelte 4 slot migrations.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Build flexible components in Svelte 5 using snippets, {@render}, typed children props, and named content areas
Provides Svelte 5.0 knowledge patch on runes ($state, $derived, $effect, $props), snippets replacing slots, callback props for events, mount/hydrate API. Auto-loads for Svelte tasks.
Provides examples for Svelte 5 runes ($state, $derived), bindable props, snippets, SvelteKit load functions and form actions, plus Svelte 4 migration guidance.
Guides Svelte 5 best practices for reactivity using $state, $derived, $effect, $props; covers event handling, styling, library integration. For writing/editing Svelte components.
Share bugs, ideas, or general feedback.
Build flexible components in Svelte 5 using snippets, {@render}, typed children props, and named content areas
<slot> patterns to Svelte 5 snippetsSvelte 5 snippets — defining reusable template blocks:
{#snippet name(params)} and render them with {@render name()}:{#snippet greeting(name: string)}
<p>Hello, <strong>{name}</strong>!</p>
{/snippet}
{@render greeting('Alice')}
{@render greeting('Bob')}
Children prop — default content slot:
children prop (typed as Snippet):<!-- Card.svelte -->
<script lang="ts">
import type { Snippet } from 'svelte'
let { children }: { children: Snippet } = $props()
</script>
<div class="card">
{@render children()}
</div>
<!-- Usage -->
<Card>
<p>This content is passed as children.</p>
</Card>
<script lang="ts">
import type { Snippet } from 'svelte'
let { children }: { children?: Snippet } = $props()
</script>
{#if children}
{@render children()}
{:else}
<p>No content provided.</p>
{/if}
Named snippets — multiple content areas:
<!-- Dialog.svelte -->
<script lang="ts">
import type { Snippet } from 'svelte'
let {
title,
children,
footer
}: {
title: Snippet
children: Snippet
footer?: Snippet
} = $props()
</script>
<dialog>
<header>{@render title()}</header>
<main>{@render children()}</main>
{#if footer}
<footer>{@render footer()}</footer>
{/if}
</dialog>
<!-- Usage -->
<Dialog>
{#snippet title()}
<h2>Confirm Delete</h2>
{/snippet}
<p>Are you sure you want to delete this item?</p>
{#snippet footer()}
<button onclick={cancel}>Cancel</button>
<button onclick={confirm}>Delete</button>
{/snippet}
</Dialog>
Snippets with parameters — render prop pattern:
<!-- List.svelte -->
<script lang="ts">
import type { Snippet } from 'svelte'
let {
items,
renderItem
}: {
items: Item[]
renderItem: Snippet<[Item, number]>
} = $props()
</script>
<ul>
{#each items as item, index}
<li>{@render renderItem(item, index)}</li>
{/each}
</ul>
<!-- Usage -->
<List {items}>
{#snippet renderItem(item, index)}
<span>{index + 1}. {item.name}</span>
{/snippet}
</List>
Svelte 4 migration — slots to snippets:
<slot> with {@render children()} and named slots with named snippet props:| Svelte 4 | Svelte 5 |
|---|---|
<slot /> | {@render children()} |
<slot name="header" /> | {@render header()} |
<slot {item} /> | {@render renderItem(item)} |
<svelte:fragment slot="header"> | {#snippet header()} |
Passing snippets programmatically:
renderSnippet from svelte when you need to render a snippet in a non-template context (e.g., passing to a library):import { renderSnippet } from 'svelte';
const rendered = renderSnippet(mySnippet, args);
Snippets vs. components:
Snippets are template fragments — they share the reactive scope of their defining component. Components are isolated scopes with their own lifecycle. Use snippets when you need to inject templating into a parent component; use child components when you need encapsulation.
Snippet type signatures:
import type { Snippet } from 'svelte';
Snippet; // no params
Snippet<[]>; // explicit no params
Snippet<[string]>; // one string param
Snippet<[Item, number]>; // two params
{@render} with null safety:
{@render} will throw if passed undefined. Always check optionality:
{#if footer}
{@render footer()}
{/if}
Or use the ?? pattern with a fallback snippet.
Svelte 4 backward compatibility:
<slot> still works in Svelte 5 but is deprecated. The children snippet prop is backward-compatible — components using {@render children()} can receive content from both Svelte 5 snippet syntax and legacy <slot> fallback in child components.
Composing deeply nested layouts:
For deeply nested layouts (e.g., shell > sidebar > content), pass snippet props down or use the context API to avoid prop threading. The context API allows any descendant to read values without passing through intermediate components.
https://svelte.dev/docs/svelte/snippet