
Svelte Core Bestpractices
Apply Svelte 5 {@attach} patterns with correct effect lifecycle and reusable attachment factories when building interactive UI.
Overview
Svelte Core Best Practices is an agent skill for the Build phase that documents Svelte 5 {@attach} lifecycle, cleanup, and attachment-factory patterns for DOM behavior.
Install
npx skills add https://github.com/sveltejs/ai-tools --skill svelte-core-bestpracticesWhat is this skill?
- Documents {@attach} running inside $effect on mount and when $state read inside the attachment updates
- Explains optional cleanup return functions before re-run and on DOM removal
- Covers attachment factories (e.g. tooltip) that return Attachment functions for reuse
- Notes multiple attachments per element and Svelte 5.29+ availability
- Shows reactive re-creation when factory arguments or in-function state change
- Attachments require Svelte 5.29 or newer
- Elements may declare multiple {@attach} handlers
Adoption & trust: 2.1k installs on skills.sh; 266 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You need Svelte-native DOM side effects without leaking listeners or fighting $effect timing when state updates.
Who is it for?
Shipping Svelte 5.29+ UIs that wrap DOM libraries or custom element behaviors behind reusable attachment factories.
Skip if: Projects still on Svelte 4, teams avoiding Svelte 5 runes/effects, or backend-only work with no component markup.
When should I use this skill?
Building or reviewing Svelte 5 components that use {@attach}, attachment factories, or DOM cleanup tied to $state updates.
What do I get? / Deliverables
Your agent applies {@attach} and factory-returned attachments with documented cleanup so UI integrations stay reactive and teardown-safe.
- Attachment functions and factories with documented teardown
- Component examples using {@attach} for third-party DOM widgets
Recommended Skills
Journey fit
Attachments are a Svelte markup and client-runtime API; the canonical home for UI framework guidance is Build → frontend. Subphase frontend covers component APIs, DOM behavior, and reactive styling hooks—not backend or ship-time QA.
How it compares
Use for Svelte’s {@attach} contract instead of copying React useEffect or generic “mount hook” snippets that ignore Svelte 5 reactivity.
Common Questions / FAQ
Who is svelte-core-bestpractices for?
Solo and indie builders using Svelte 5 who want correct attachment lifecycle, cleanup, and factory patterns while coding components with an AI agent.
When should I use svelte-core-bestpractices?
During Build → frontend when adding {@attach}, binding reactive content into attachments, or refactoring imperative DOM code into attachment factories for tooltips, observers, or similar widgets.
Is svelte-core-bestpractices safe to install?
It is documentation-style procedural knowledge with no built-in network or shell requirements; review the Security Audits panel on this Prism page before trusting any third-party skill package.
SKILL.md
READMESKILL.md - Svelte Core Bestpractices
Attachments are functions that run in an [effect]($effect) when an element is mounted to the DOM or when [state]($state) read inside the function updates. Optionally, they can return a function that is called before the attachment re-runs, or after the element is later removed from the DOM. > [!NOTE] > Attachments are available in Svelte 5.29 and newer. ```svelte <!--- file: App.svelte ---> <script> /** @type {import('svelte/attachments').Attachment} */ function myAttachment(element) { console.log(element.nodeName); // 'DIV' return () => { console.log('cleaning up'); }; } </script> <div {@attach myAttachment}>...</div> ``` An element can have any number of attachments. ## Attachment factories A useful pattern is for a function, such as `tooltip` in this example, to _return_ an attachment (demo: ```svelte <!--- file: App.svelte ---> <script> import tippy from 'tippy.js'; let content = $state('Hello!'); /** * @param {string} content * @returns {import('svelte/attachments').Attachment} */ function tooltip(content) { return (element) => { const tooltip = tippy(element, { content }); return tooltip.destroy; }; } </script> <input bind:value={content} /> <button {@attach tooltip(content)}> Hover me </button> ``` Since the `tooltip(content)` expression runs inside an [effect]($effect), the attachment will be destroyed and recreated whenever `content` changes. The same thing would happen for any state read _inside_ the attachment function when it first runs. (If this isn't what you want, see [Controlling when attachments re-run](#Controlling-when-attachments-re-run).) ## Inline attachments Attachments can also be created inline (demo: ```svelte <!--- file: App.svelte ---> <canvas width={32} height={32} {@attach (canvas) => { const context = canvas.getContext('2d'); $effect(() => { context.fillStyle = color; context.fillRect(0, 0, canvas.width, canvas.height); }); }} ></canvas> ``` > [!NOTE] > The nested effect runs whenever `color` changes, while the outer effect (where `canvas.getContext(...)` is called) only runs once, since it doesn't read any reactive state. ## Conditional attachments Falsy values like `false` or `undefined` are treated as no attachment, enabling conditional usage: ```svelte <div {@attach enabled && myAttachment}>...</div> ``` ## Passing attachments to components When used on a component, `{@attach ...}` will create a prop whose key is a [`Symbol`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol). If the component then [spreads](/tutorial/svelte/spread-props) props onto an element, the element will receive those attachments. This allows you to create _wrapper components_ that augment elements (demo: ```svelte <!--- file: Button.svelte ---> <script> /** @type {import('svelte/elements').HTMLButtonAttributes} */ let { children, ...props } = $props(); </script> <!-- `props` includes attachments --> <button {...props}> {@render children?.()} </button> ``` ```svelte <!--- file: App.svelte ---> <script> import tippy from 'tippy.js'; import Button from './Button.svelte'; let content = $state('Hello!'); /** * @param {string} content * @returns {import('svelte/attachments').Attachment} */ function tooltip(content) { return (element) => { const tooltip = tippy(element, { content }); return tooltip.destroy; }; } </script> <input bind:value={content} /> <Button {@attach tooltip(content)}>Hover me</Button> ``` ## Controlling when attachments re-run Attachments, unlike [actions](use), are fully reactive: `{@attach foo(bar)}` will re-run on changes to `foo` _or_ `bar` (or any state read inside `foo`): ```js // @errors: 7006 2304 2552 function foo(bar) { return (node) => { veryExpensiveSetupWork(node); update(node, bar); }; } ``` In the rare case that this is a problem (for example, if `foo` does expensive and unavoidable setup work) consider passing the data inside a function a