Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: attachments #15000

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open

feat: attachments #15000

wants to merge 19 commits into from

Conversation

Rich-Harris
Copy link
Member

@Rich-Harris Rich-Harris commented Jan 13, 2025

What?

This PR introduces attachments, which are essentially a more flexible and modern version of actions.

Why?

Actions are neat but they have a number of awkward characteristics and limitations:

  • the syntax is very weird! <div use:foo={bar}> implies some sort of equality between foo and bar but actually means foo(div, bar). There's no way you could figure that out just by looking at it
  • the foo in use:foo has to be an identifier. You can't, for example, do use:createFoo() — it must have been declared elsewhere
  • as a corollary, you can't do 'inline actions'
  • it's not reactive. If foo changes, use:foo={bar} does not re-run. If bar changes, and foo returned an update method, that method will re-run, but otherwise (including if you use effects, which is how the docs recommend you use actions) nothing will happen
  • you can't use them on components
  • you can't spread them, so if you want to add both attributes and behaviours you have to jump through hoops

We can do much better.

How?

You can attach an attachment to an element with the {@attach fn} tag (which follows the existing convention used by things like {@html ...} and {@render ...}, where fn is a function that takes the element as its sole argument:

<div {@attach (node) => console.log(node)}>...</div>

This can of course be a named function, or a function returned from a named function...

<button {@attach tooltip('Hello')}>
  Hover me
</button>

...which I'd expect to be the conventional way to use attachments.

Attachments can be create programmatically and spread onto an object:

<script>
  const stuff = {
    class: 'cool-button',
    onclick: () => console.log('clicked'),
    [Symbol()]: (node) => alert(`I am a ${node.nodeName}`)
  };
</script>

<button {...stuff}>hello</button>

As such, they can be added to components:

<Button
  class="cool-button"
  onclick={() => console.log('clicked')}
  {@attach (node) => alert(`I am a ${node.nodeName}`)}
>
  hello
</Button>
<script>
  let { children, ...props } = $props();
</script>

<button {...props}>{@render children?.()}</button>

Since attachments run inside an effect, they are fully reactive.

I haven't figured out if it should be possible to return a cleanup function directly from an attachment, or if you should need to create a child effect

Because you can create attachments inline, you can do cool stuff like this, which is somewhat more cumbersome today.

When?

As soon as we bikeshed all the bikesheddable details.


While this is immediately useful as a better version of actions, I think the real fun will begin when we start considering this as a better version of transitions and animations as well. Today, the in:/out:/transition: directives are showing their age a bit. They're not very composable or flexible — you can't put them on components, they generally can't 'talk' to each other except in very limited ways, you can't transition multiple styles independently, you can't really use them for physics-based transitions, you can only use them on DOM elements rather than e.g. objects in a WebGL scene graph, and so on.

Ideally, instead of only having the declarative approach to transitions, we'd have a layered approach that made that flexibility possible. Two things in particular are needed: a way to add per-element lifecycle functions, and an API for delaying the destruction of an effect until some work is complete (which outro transitions uniquely have the power to do today). This PR adds the first; the second is a consideration for our future selves.

Before submitting the PR, please make sure you do the following

  • It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
  • Prefix your PR title with feat:, fix:, chore:, or docs:.
  • This message body should clearly illustrate what problems it solves.
  • Ideally, include a test that fails without this PR but passes with it.
  • If this PR changes code within packages/svelte/src, add a changeset (npx changeset).

Tests and linting

  • Run the tests with pnpm test and lint the project with pnpm lint

Copy link

changeset-bot bot commented Jan 13, 2025

🦋 Changeset detected

Latest commit: 6402161

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
svelte Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@Rich-Harris
Copy link
Member Author

preview: https://svelte-dev-git-preview-svelte-15000-svelte.vercel.app/

this is an automated message

Copy link
Contributor

Playground

pnpm add https://pkg.pr.new/svelte@15000

@huntabyte
Copy link
Member

Would something like this work as well?

<script>
  	import { createAttachmentKey } from 'svelte/attachments';

  	const stuff = {
    	class: 'cool-button',
    	onclick: () => console.log('clicked'),
    	[createAttachmentKey()]: (node) => console.log(`I am one attachment`)
  	};

	const otherStuff = {
		[createAttachmentKey()]: (node) => console.log('I am another attachment')
	}
</script>

<button {...stuff} {...otherStuff}>hello</button>

Where the result on mount would be:

I am one attachment
I am another attachment

@JonathonRP
Copy link

Personally I would prefer a createAttachment like createSnippet. Just something to consider for the team

@Leonidaz
Copy link

nice! 👍

I wonder if it would be more flexible for composition if the syntax can work with named props.

programmatically:

<script>
  // reverse logic instead of symbol-ed key, a symbol-ed function wrapper
  import { createAttachment } from 'svelte/attachments';

  const stuff = {
    class: 'cool-button',
    onclick: () => console.log('clicked'),
    showAlert: createAttachment((node) => alert(`I am a ${node.nodeName}`)),
    logger: createAttachment((node) => console.log(`I am a ${node.nodeName}`)),
  };
</script>

<button {...stuff}>hello</button>

directly on components:

<Button
  class="cool-button"
  onclick={() => console.log('clicked')}
  showAlert={@attach (node) => alert(`I am a ${node.nodeName}`)}
  logger={@attach (node) => console.log(`I am a ${node.nodeName}`)}
>
  hello
</Button>

and spread in which case at runtime the prop values can be checked for a special attach symbol (the prop key names are irrelevant)

<script>
  let { children, ...props } = $props();
</script>

<button {...props}>{@render children?.()}</button>

or explicitly declare props, for further composition (and it would be nice for TypeScript declarations):

<script>
  import AnotherComponent from './AnotherComponent.svelte';
  let { children, showAlert, logger } = $props();
</script>

<button {@attach showAlert} {@attach logger}>{@render children?.()}</button>

<AnotherComponent logger={@attach logger} />

And with either syntax, one could also just pass in a prop as an "attachable" function without {@attach} syntax if they're going to eventually explicitly attach it to a DOM element without spreading.

<AnotherComponent {logger}  myAction={(node) => { /* do something */ } />
<!-- AnotherComponent.svelte -->
<script>
  let { logger, myAction } = $props();
</script>

<input {@attach logger} {@attach myAction}>

@mr-josh
Copy link

mr-josh commented Jan 14, 2025

Could svelte have a set of constant symbols (assuming we're using the Symbol API)? Could also allow for updating the transition directives.

Something like:

<script>
  import { ATTACHMENT_SYMBOL, TRANSITION_IN_SYMBOL } from "svelte/symbols";
  import { fade } from "svelte/transition";

  const stuff = {
    [ATTACHMENT_SYMBOL]: (node) => console.log("hello world"),
    [TRANSITION_IN_SYMBOL]: (node) => fade(node, { duration: 100 }),
  }; 
</script>

<button {...stuff}>hello</button>

@Conduitry
Copy link
Member

The purpose of having a function that returns symbols - rather than using a single symbol - is that it lets you have multiple attachments on a single element/component without them clobbering one another.

@JonathonRP
Copy link

The current rub with transitions is their css and/or tick methods that apply to style but if transitions were just attachments that modified the style attribute of node then they would just be attachments too...

@Thiagolino8
Copy link

The current rub with transitions is their css and/or tick methods that apply to style but if transitions were just attachments that modified the style attribute of node then they would just be attachments too...

Actions can already do this already, the advantage of transitions is to do this outside the main thread
In svelte 3/4 creating css transitions and in svelte 5 using the WAAPI

@Thiagolino8
Copy link

Thiagolino8 commented Jan 14, 2025

One of the advantages of the special syntax of actions was the fact that it generated shakable tree code

Attachments do not seem to have this advantage since every element needs to look for properties with the special symbol for special behavior

@Thiagolino8
Copy link

If I understand correctly, it is not possible to extract an attachment from the props and consequently it is also not possible to prevent an attachment from being passed to an element with spread props, using an attachment on a component is basically a redirect all

@JonathonRP
Copy link

The current rub with transitions is their css and/or tick methods that apply to style but if transitions were just attachments that modified the style attribute of node then they would just be attachments too...

Actions can already do this already, the advantage of transitions is to do this outside the main thread
In svelte 3/4 creating css transitions and in svelte 5 using the WAAPI

True, I'm curious about the waapi usage

@mr-josh
Copy link

mr-josh commented Jan 14, 2025

The purpose of having a function that returns symbols - rather than using a single symbol - is that it lets you have multiple attachments on a single element/component without them clobbering one another.

I'd be curious about the intention of this, cause intuitively I would assume using the function would override any previous definitions the same way standard merging of objects would. Allowing multiple of what at face value feels like the same key feels like it'll trip people up.
I think if you wanted to have multiple attachments through props, using a sequence function would be a lot clearer and guarantee what order attachments occur in.

<script>
  import { ATTACHMENT_SYMBOL } from "svelte/symbols";
  import { sequence } from "svelte/attachments";

  const attachmentA = (node) => console.log("first attachment");
  const attachmentA = (node) => console.log("second attachment");

  const stuff = {
    [ATTACHMENT_SYMBOL]: sequence(attachmentA, attachmentB),
  }; 
</script>

<button {...stuff}>hello</button>

@Rich-Harris
Copy link
Member Author

@huntabyte

Would something like this work as well?

try it and see :)

I wonder if it would be more flexible for composition if the syntax can work with named props.

You're just describing normal props! The {@attach ...} keyword is only useful when it's anonymous.

<MyComponent {@attach anonymousAttachment} named={namedAttachment} />
<script>
  let { named, ...props } = $props();
</script>

<div {@attach named} {...props} />

One of the advantages of the special syntax of actions was the fact that it generated shakable tree code

I don't follow? The only treeshaking that happens, happens in SSR mode — i.e. <div use:foo> doesn't result in foo being called on the server. That remains true for attachments. The additional runtime code required to support attachments is negligible.

If I understand correctly, it is not possible to extract an attachment from the props

It's deliberate that if you use {...stuff} that attachments will be included in that. If you really want to remove them it's perfectly possible, it's just an object with symbols. Create a derived that filters the symbols out if you need to, though I'm not sure why you'd want that.

Allowing multiple of what at face value feels like the same key feels like it'll trip people up.

Most of the time you're not interacting with the 'key', that's an advanced use case. You're just attaching stuff:

<div {attach foo()} {@attach bar()} {@attach etc()}>...</div>

One possibility for making that more concise is to allow a sequence...

<div {attach foo(), bar(), etc()}>...</div>

...but I don't know if that's a good idea.

@kran6a
Copy link

kran6a commented Jan 14, 2025

Love the proposal and how it simplified actions, specially the handler having a single parameter, which will not only encourage but force writing more composable attachments via HOFs.
i.e:

export const debounce = (cb: ()=>void)=>(ms: number)=>(element: HTMLElement)=>{
    // implementation intentionally left blank
}
<script lang="ts">
    const debounced_alert = debounce(()=>alert("You type too slow"));
</script>
<textarea {@attach debounced_alert(2000)}></textarea>

Personally I would prefer a block syntax rather than the PR one.

<!--Applies both attachments to input and textarea-->
{#attachment debounce(()=>alert("You type too slow"))(2000), debounce(()=>alert("Server is still waiting for input"))(3000)}
    <input type="text"/>
    <textarea></textarea>
{/attachment}

My reasons to prefer a block are:

  1. It is an already known syntax
  2. Easily discoverable via intellisense when you type {# (to write any other block) and the autocomplete brings up attachment as one of the options. I don't think anybody that does not know about attachments is going to discover the PR syntax via intellisense.
  3. Blocks are easier to read when the block content is a big tree since you can see the opening and closing. This is useful when the element that has the attachment is not an input/button but a clickoutside or a keydown on a whole page section.
  4. Syntax is cleaner even if you inline the attachment configuration options as otherwise they would be on the same line as 20 tailwind classes, an id, name, data- and aria- attributes.
  5. The {@something} syntax already exists and, until now, it could only be used inside an element/block, be it declaring a block-scoped constant with {@const}, rawdogging html with {@html}, or rendering with {@render}.
    Even debugging with {@debug} cannot be used in the opening tag like {@attachment}. This breaks sytax consistency and did not happen with the old use: syntax as the old something: syntax was always used on the opening tag of an element.

@Ocean-OS
Copy link
Contributor

I like this, my only concern is the similarity in syntax between this and logic tags. It may make new developers think that something like this is valid Svelte:

<div {@const ...}>

Or may make them try to do something like this:

<element>
{@attach ...}
</element>

@itsmikesharescode
Copy link

I love it!

@Theo-Steiner
Copy link
Contributor

Theo-Steiner commented Jan 14, 2025

Great iteration on actions, I love it!

Came here to ask how I could determine if an object property was an attachment, but looking at the source, it looks like isAttachmentKey is a helper to do just that!

Two things in particular are needed: a way to add per-element lifecycle functions, and an API for delaying the destruction of an effect until some work is complete (which outro transitions uniquely have the power to do today). This PR adds the first; the second is a consideration for our future selves.

I think it would be really intuitive if attachments could return an async cleanup function with the element/component being removed once the cleanup function (and all nested cleanup functions) settle/resolve!

@JonathonRP
Copy link

I agree, I think we're over thinking the usability of attachments. The only case I could think of is the ability to destructor from an object holding command reusable attachment functions and taking out some and spreading the rest, as pointed out previously. But again that could be easily added later. I think the conversation should pivot to helping discuss about transition and animation handling/ implementation if there should be continued discussions here at all.

@Leonidaz
Copy link

Leonidaz commented Jan 16, 2025

{------ expression ------}
 decorator  |  expression
{@render        snippet()}
{@html             source}
{@debug          variable}
{@const something = 'something'}

Now that we have runes in v5, I would get rid of this syntax and use runes instead and in general stick more to the javascript syntax. The only kind of an issue is that stores use the $ syntax, but I doubt very much these names would conflict with people's codebases.

{$render(snippet())} 
{$html(source)}
{$debug(variable)} 
{const something = 'something'} 

Likely for @const, the compiler can just look for const. worst case {$const something = 'something'}

So, back to attachments, transitions, animations, etc. I wouldn't even add anything special and just treat them as regular props.

for anonymous props just let the compiler convert unnamed props to anonymous props

import { attach } from 'svelte/attachments';
import { transition } from 'svelte/transitions';

<Component
  {attach((node) => {})}
  named={attach((node) => {}}
  {transition((action, options) => {})}
  fader={transition((action, options) => {})}
/>

all unnamed props, the compiler just compiles to:

const props = {[Symbol()]: attach((node) => {}), [Symbol()]: transition((action, options) => {})};

@PuruVJ
Copy link
Collaborator

PuruVJ commented Jan 16, 2025

@Leonidaz This would be a huge and unnecessary breaking change, and will just make everything far more confusing. Where does the boundary of runes start, where does it end? What even is a rune. Right now you can define runes clearly, with this you cannot.

Coming back to the point, this discussion is about attachment syntax and discussions related only to this topic, not about changing entire framework's syntax :)

@Leonidaz
Copy link

Coming back to the point, this discussion is about attachment syntax and discussions related only to this topic, not about changing entire framework's syntax :)

@PuruVJ

This was in context, still on the subject and in reply to another comment. This lays out my thinking about using the special @ symbol which was a part of this PR.

And if you notice, the second half of my comment talks specifically about attachments.

This would be a huge and unnecessary breaking change, and will just make everything far more confusing. Where does the boundary of runes start, where does it end? What even is a rune. Right now you can define runes clearly, with this you cannot.

I'm not sure what you mean, take a look at $inspect, but we can discuss it somewhere else.

@dangelomedinag
Copy link

@Leonidaz I think this is a topic to be addressed at another time.

For now I'll just say to look back to see how future syntax can be more consistent, learning from syntax that didn't age well. "Optimise for Vibes" is a notable strength of Svelte. The substance and the form matter too.

@webJose
Copy link
Contributor

webJose commented Jan 16, 2025

@Rich-Harris after test-driving this thing, I noticed that attachments are a way to reach the onMount and onDestroy events of components. I'm not complaining, and I certainly don't see a problem with this, but I thought I should at least mention this.

@paoloricciuti
Copy link
Member

@Rich-Harris after test-driving this thing, I noticed that attachments are a way to reach the onMount and onDestroy events of components. I'm not complaining, and I certainly don't see a problem with this, but I thought I should at least mention this.

Wdym by this?

@Leonidaz
Copy link

Leonidaz commented Jan 16, 2025

I think this is a topic to be addressed at another time.

For now I'll just say to look back to see how future syntax can be more consistent, learning from syntax that didn't age well. "Optimise for Vibes" is a notable strength of Svelte. The substance and the form matter too.

@dangelomedinag
maybe there is an issue with github, it can be glitchy. Or maybe my original post was cutoff. Or maybe github hides a lot of previous comments where there was more context. Did you read my reply to PuruVJ? #15000 (comment)

Again, I specifically talk about the attachment syntax in my original comment.

For attachments, transitions, animations, etc. I wouldn't even add anything special and just treat them as regular props.

for anonymous props just let the compiler convert unnamed props to anonymous props

import { attach } from 'svelte/attachments';
import { transition } from 'svelte/transitions';

<Component
  {attach((node) => {})}
  named={attach((node) => {}}
  {transition((action, options) => {})}
  fader={transition((action, options) => {})}
/>

all unnamed props, the compiler just compiles to:

const props = {[Symbol()]: attach((node) => {}), [Symbol()]: transition((action, options) => {})};

#15000 (comment)

@webJose
Copy link
Contributor

webJose commented Jan 16, 2025

@Rich-Harris after test-driving this thing, I noticed that attachments are a way to reach the onMount and onDestroy events of components. I'm not complaining, and I certainly don't see a problem with this, but I thought I should at least mention this.

Wdym by this?

This:

			<Button onclick={() => shown = !shown} {@attach ref} {@attach () => () => shown = false}>
				A: I have a tooltip!  Click me to show it.
			</Button>

I wrote the second attach to hide the tooltip once the component unmounts.

@Leonidaz
Copy link

Leonidaz commented Jan 16, 2025

			<Button onclick={() => shown = !shown} {@attach ref} {@attach () => () => shown = false}>
				A: I have a tooltip!  Click me to show it.
			</Button>

@webJose
This is intended (e.g. if you add an event listener). The function runs inside an effect and the effect returns the result of the function. So, you might as well return the 'onDestroy' in the first attachment.

@webJose
Copy link
Contributor

webJose commented Jan 16, 2025

@JonathonRP It's in the POC for floating-ui that I linked to earlier.

@webJose
Copy link
Contributor

webJose commented Jan 16, 2025

@Leonidaz is much more code to reuse the return of the first {@attach}.

@JonathonRP
Copy link

@Leonidaz is much more code to reuse the return of the first {@attach}.

@webJose

<Button onclick={() => shown = !shown} {@attach ref(destroy: () => shown = false)}>
A: I have a tooltip!  Click me to show it.
</Button>

@webJose
Copy link
Contributor

webJose commented Jan 16, 2025

@Leonidaz is much more code to reuse the return of the first {@attach}.

@webJose

<Button onclick={() => shown = !shown} {@attach ref(destroy: () => shown = false)}>
A: I have a tooltip!  Click me to show it.
</Button>

The ref function is incorrectly called in your snippet.

@JonathonRP
Copy link

The ref function is incorrectly called in your snippet.

 <Button onclick={() => shown = !shown} {@attach ref(destroy: () => shown = false)}>
 A: I have a tooltip!  Click me to show it.
 </Button>
export ref(destroy: () => void) {
    return () => destroy
}

@webJose this is sudo code for an idea not exactly copy/paste. but idea is make ref be a function that takes a function and runs original code but returns the function it took in for destroy logic

@paoloricciuti
Copy link
Member

But most importantly it's not the unmount of the component but the unmount of the element where the attachments are spreaded

@webJose
Copy link
Contributor

webJose commented Jan 16, 2025

Ok, yes. That's true. And only if the component opts to spread it. I, as consumer of the component, have no saying in what the component actually does with the attachment.

No worries here, then. 👍

@webJose
Copy link
Contributor

webJose commented Jan 16, 2025

@Rich-Harris I think I have only one more ask: Typing.

export type CleanupFn = () => void; // This is used in a lot of places across Svelte, not just attachments.
export type AttachmentFn = (node: HTMLElement) => void | CleanupFn; // Or Node instead of HTMLElement?  I never know.

If, in the future, the shape of attachment functions change, we don't have to change our own private typings.

@khromov
Copy link
Contributor

khromov commented Jan 17, 2025

It would be nice to consider the ergonomics of introducing @, it is much harder to write on a european keyboard (having to do a one-hand dance between Opt+2) versus something like : or + that is much easier to write from a neutral hand position. (+ having the bonus of being possible to write without a button combination)

Eg:

<div {:attach (node) => console.log(node)}>...</div>
or
<div {+attach (node) => console.log(node)}>...</div>

@webJose
Copy link
Contributor

webJose commented Jan 17, 2025

@khromov I think you're too late with that argument, since "@" has been long established in other parts of Svelte for years.

But your message brings the possibility of a feature request? Make, as much as possible, configurable symbols. Maybe compiler options could define the "shape" of keywords? This, of course, would be unrelated to this PR.

@Ocean-OS
Copy link
Contributor

But your message brings the possibility of a feature request? Make, as much as possible, configurable symbols. Maybe compiler options could define the "shape" of keywords? This, of course, would be unrelated to this PR.

The thing about having configurable syntax to that extent is that it makes every Svelte app and component's source code very different and confusing. If you use someone else's component, you'd also have to use whatever syntax choices they used as well.

@webJose
Copy link
Contributor

webJose commented Jan 17, 2025

You're probably right, @Ocean-OS. I was trying to be helpful. I guess it is a long shot.

@gterras
Copy link

gterras commented Jan 17, 2025

it is much harder to write on a european keyboard (having to do a one-hand dance between Opt+2)

"European keyboards" are not a thing, there are dozens of different ISO layouts each with their own specificity, and each OS also has their own specific sub-layout (like the @ position), but most importantly no key should be much harder to type than the others on your keyboard anyway.

@dangelomedinag
Copy link

dangelomedinag commented Jan 17, 2025

console.log is displayed first inside an attachment and then console.log when the component is mounted.
if you use $effect inside the attachment it works as you expect

playground

@Leonidaz
Copy link

console.log is displayed first inside an attachment and then console.log when the component is mounted. if you use $effect inside the attachment it works as you expect

@dangelomedinag

This is how it works in svelte in general, the template effects run first, otherwise $effect.pre should be used.

template effects run first

@bcharbonnier
Copy link

Controversial idea: DOM runes ?

We do have JS runes to interact with lifecycle of javascript objects. What about DOM runes to interact with DOM elements lifecycle ?

We already have bind:this (except if you also want to kill that one ??). Let's drop all the fuss about finding a weird template declarative syntax for it: everything occurs in the script with imperative syntax assuming you have a reference to the node.

At component level, nothing change, you can just pass whatever prop you want, you name it the way you want, these are just functions in the end.


🙏 Please don't say this is not the topic, you're off topic, the syntax is already somewhat agreed on.
This is clearly the topic, everyone loves the feature, but most people being nice giving feedback complain that the syntax is far from being the best.

@paoloricciuti
Copy link
Member

Controversial idea: DOM runes ?

We do have JS runes to interact with lifecycle of javascript objects. What about DOM runes to interact with DOM elements lifecycle ?

We already have bind:this (except if you also want to kill that one ??). Let's drop all the fuss about finding a weird template declarative syntax for it: everything occurs in the script with imperative syntax assuming you have a reference to the node.

At component level, nothing change, you can just pass whatever prop you want, you name it the way you want, these are just functions in the end.

🙏 Please don't say this is not the topic, you're off topic, the syntax is already somewhat agreed on. This is clearly the topic, everyone loves the feature, but most people being nice giving feedback complain that the syntax is far from being the best.

The main idea behind this was exactly to prevent you to go to the script to do simple Dom manipulations...you can basically already do this with actions

@Hugos68
Copy link

Hugos68 commented Jan 18, 2025

Controversial idea: DOM runes ?

Normal runes are already DOM runes because their state is updated in the DOM, what you're saying can already be achieved in whatever way you want using runes. Not sure what the feature of a DOM rune would be.

@bcharbonnier
Copy link

For me runes are designed for JavaScript objects. It's like Signals to notify listeners when properties change on objects.
It happens that the first effect is run when the entire dom of a component is appended, but it has nothing to do with DOM on its own.

What I suggest is runes for when DOM nodes are inserted, removed and updated. Just like we could do it with a giant MutationObserver, but thanks to Svelte being a compiler we can know when these insertions/deletions occurred at build time, and associate functions provided with them. No need for the Observer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.