All skills
sveltejs avatar

/svelte-core-bestpractices

@1eb06fd official
by Sveltesveltejs/mcp331 stars
40

Guidance on writing fast, robust, modern Svelte code. Load this skill whenever in a Svelte project and asked to write/edit or analyze a Svelte component or module. Covers reactivity, event handling, styling, integration with libraries and more.

Use this Skill: https://skilld.dev/gh/sveltejs/mcp/svelte-core-bestpractices

This session only. Nothing lands on disk.

referencesattach.md

≈1.2k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Attachments are functions that run in an effect when an element is mounted to the DOM or when 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.

<!--- 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:

<!--- 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, 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.)

Inline attachments

Attachments can also be created inline (demo:

<!--- 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:

<div {@attach enabled && myAttachment}>...</div>

Passing attachments to components

When used on a component, {@attach ...} will create a prop whose key is a Symbol. If the component then spreads props onto an element, the element will receive those attachments.

This allows you to create wrapper components that augment elements (demo:

<!--- file: Button.svelte --->
<script>
	/** @type {import('svelte/elements').HTMLButtonAttributes} */
	let { children, ...props } = $props();
</script>

<!-- `props` includes attachments -->
<button {...props}>
	{@render children?.()}
</button>
<!--- 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, are fully reactive: {@attach foo(bar)} will re-run on changes to foo or bar (or any state read inside foo):

// @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 and reading it in a child effect:

// @errors: 7006 2304 2552
function foo(+++getBar+++) {
	return (node) => {
		veryExpensiveSetupWork(node);

+++		$effect(() => {
			update(node, getBar());
		});+++
	}
}

Creating attachments programmatically

To add attachments to an object that will be spread onto a component or element, use createAttachmentKey.

Converting actions to attachments

If you're using a library that only provides actions, you can convert them to attachments with fromAction, allowing you to (for example) use them with components.

Source: SKILL.md on GitHub

No alerts16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The skill provides legitimate guidance and best practices for Svelte 5 development. It contains only documentation and code examples with no identified security risks.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer6mo

    10 files scanned · No issues

  • ZeroLeaks5mo

    1 finding · Score: 86/100

Signed by skilld at 1eb06fd. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last week.

Activeupdated 2 months ago
  • svelte
  • reactivity
  • runes
  • components
  • styling
  • state-management
  • event-handling
  • best-practices
  • svelte5

README badge

README badge for sveltejs/mcp/svelte-core-bestpractices

Provides best practices for writing Svelte 5 components using runes (`$state`, `$derived`, `$effect`, `$props`), event handling, snippets, styling, and context. Load this skill in Svelte projects when writing or reviewing components to ensure idiomatic reactivity patterns and avoid legacy features.

Generated from the current SKILL.md.

Should I use $state for all variables?
No. Only use $state for variables that should be reactive and trigger updates in effects, derived values, or templates. Regular variables can stay as normal JavaScript. For large objects that are only reassigned (not mutated), use $state.raw instead to avoid proxy overhead.
When should I use $derived instead of $effect?
$derived should be your first choice for computing values from state. Use $effect only as an escape hatch for side effects like syncing to external libraries. Never use $effect just to update a variable based on state changes.
Can I use $effect to listen to window or document events?
No. Use <svelte:window> and <svelte:document> instead. Avoid $effect or onMount for attaching global event listeners.
What should I use instead of on:click and other legacy event directives?
Use the onclick attribute directly (e.g. <button onclick={() => {...}}>), which works with attribute shorthand and spread props.
Should I destructure items in each blocks if I need to bind to them?
No. Avoid destructuring if you need to mutate the item with something like bind:value={item.count}, as it can break reactivity.

Generated from the current SKILL.md. These answers refresh after source changes.