Svelte • Template syntax

{@attach ...}

On this page

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.

Attachments are available in Svelte 5.29 and newer.

<script lang="ts">
	import type { interface Attachment<T extends EventTarget = Element>

An attachment is a function that runs when an element is mounted to the DOM, and optionally returns a function that is called when the element is later removed.

It can be attached to an element with an {@attach ...} tag, or by spreading an object containing a property created with createAttachmentKey.

Attachment
} from 'svelte/attachments';
const const myAttachment: Attachment<Element>myAttachment: interface Attachment<T extends EventTarget = Element>

An attachment is a function that runs when an element is mounted to the DOM, and optionally returns a function that is called when the element is later removed.

It can be attached to an element with an {@attach ...} tag, or by spreading an object containing a property created with createAttachmentKey.

Attachment
= (element: Elementelement) => {
var console: Consoleconsole.Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
(element: Elementelement.Node.nodeName: string

The read-only nodeName property of Node returns the name of the current node as a string.

MDN Reference

nodeName
); // 'DIV'
return () => { var console: Consoleconsole.Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
('cleaning up');
}; }; </script> <div {@attach const myAttachment: Attachment<Element>myAttachment}>...</div>
<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):

<script lang="ts">
	import tippy from 'tippy.js';
	import type { Attachment } from 'svelte/attachments';

	let content = $state('Hello!');

	function tooltip(content: string): Attachment {
		return (element) => {
			const tooltip = tippy(element, { content });
			return tooltip.destroy;
		};
	}
</script>

<input bind:value={content} />

<button {@attach tooltip(content)}>
	Hover me
</button>
<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):

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

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

<script lang="ts">
	import type { HTMLButtonAttributes } from 'svelte/elements';

	let { let children: Snippet<[]> | undefinedchildren, ...
let props: {
    [key: `data-${string}`]: any;
    [key: symbol]: false | Attachment<HTMLButtonElement> | null | undefined;
    disabled?: boolean | undefined | null;
    form?: string | undefined | null;
    formaction?: string | undefined | null;
    formenctype?: "application/x-www-form-urlencoded" | "multipart/form-data" | "text/plain" | undefined | null;
    formmethod?: "dialog" | "get" | "post" | "DIALOG" | "GET" | "POST" | undefined | null;
    formnovalidate?: boolean | undefined | null;
    formtarget?: string | undefined | null;
    ... 436 more ...;
    xmlns?: string | undefined | null;
}
props
}: HTMLButtonAttributes =
function $props(): any
namespace $props

Declares the props that a component accepts. Example:

let { optionalProp = 42, requiredProp, bindableProp = $bindable() }: { optionalProp?: number; requiredProps: string; bindableProp: boolean } = $props();
@see{@link https://svelte.dev/docs/svelte/$props Documentation}
$
function $props(): any
namespace $props

Declares the props that a component accepts. Example:

let { optionalProp = 42, requiredProp, bindableProp = $bindable() }: { optionalProp?: number; requiredProps: string; bindableProp: boolean } = $props();
@see{@link https://svelte.dev/docs/svelte/$props Documentation}
props
();
</script> <!-- `props` includes attachments --> <button {...
let props: {
    [key: `data-${string}`]: any;
    [key: symbol]: false | Attachment<HTMLButtonElement> | null | undefined;
    disabled?: boolean | undefined | null;
    form?: string | undefined | null;
    formaction?: string | undefined | null;
    formenctype?: "application/x-www-form-urlencoded" | "multipart/form-data" | "text/plain" | undefined | null;
    formmethod?: "dialog" | "get" | "post" | "DIALOG" | "GET" | "POST" | undefined | null;
    formnovalidate?: boolean | undefined | null;
    formtarget?: string | undefined | null;
    ... 436 more ...;
    xmlns?: string | undefined | null;
}
props
}>
{@render let children: Snippet<[]> | undefinedchildren?.()} </button>
<script>
	/** @type {import('svelte/elements').HTMLButtonAttributes} */
	let { let children: anychildren, ...let props: anyprops } = 
function $props(): any
namespace $props

Declares the props that a component accepts. Example:

let { optionalProp = 42, requiredProp, bindableProp = $bindable() }: { optionalProp?: number; requiredProps: string; bindableProp: boolean } = $props();
@see{@link https://svelte.dev/docs/svelte/$props Documentation}
$
function $props(): any
namespace $props

Declares the props that a component accepts. Example:

let { optionalProp = 42, requiredProp, bindableProp = $bindable() }: { optionalProp?: number; requiredProps: string; bindableProp: boolean } = $props();
@see{@link https://svelte.dev/docs/svelte/$props Documentation}
props
();
</script> <!-- `props` includes attachments --> <button {...let props: anyprops}> {@render let children: anychildren?.()} </button>
<script lang="ts">
	import tippy from 'tippy.js';
	import Button from './Button.svelte';
	import type { Attachment } from 'svelte/attachments';

	let content = $state('Hello!');

	function tooltip(content: string): Attachment {
		return (element) => {
			const tooltip = tippy(element, { content });
			return tooltip.destroy;
		};
	}
</script>

<input bind:value={content} />

<Button {@attach tooltip(content)}>
	Hover me
</Button>
<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):

function function foo(bar: any): (node: any) => voidfoo(bar) {
Parameter 'bar' implicitly has an 'any' type.
return (node) => {
Parameter 'node' implicitly has an 'any' type.
veryExpensiveSetupWork(node: anynode);
Cannot find name 'veryExpensiveSetupWork'.
update(node: anynode, bar: anybar);
Cannot find name 'update'. Did you mean 'Date'?
}; }

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:

function function foo(getBar: any): (node: any) => voidfoo(getBar) {
Parameter 'getBar' implicitly has an 'any' type.
return (node) => {
Parameter 'node' implicitly has an 'any' type.
veryExpensiveSetupWork(node: anynode);
Cannot find name 'veryExpensiveSetupWork'.
function $effect(fn: () => void | (() => void)): void
namespace $effect

Runs code when a component is mounted to the DOM, and then whenever its dependencies change, i.e. $state or $derived values. The timing of the execution is after the DOM has been updated. Example: ts</span> <span class="highlight add">$effect(() => console.log('The count is now ' + count));</span> <span class="highlight add"> If you return a function from the effect, it will be called right before the effect is run again, or when the component is unmounted. Does not run during server-side rendering.

@see{@link https://svelte.dev/docs/svelte/$effect Documentation}@paramfn The function to execute
$effect
(() => {
update(node: anynode, getBar: anygetBar());
Cannot find name 'update'. Did you mean 'Date'?
});
} }

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.