svelte: An element that uses the animate directive must be the immediate child of a keyed each block
Describe the problem
The animate directive is only allowed to an immediate child of a keyed each block.
{#each todos.filter(t => !t.done) as todo (todo.id)}
<label animate:flip>{todo.description}</label>
{/each}
As soon as the child might become more complex, the child element might be wrapped in its own component. That breaks the animate directive with the well known error message.
{#each todos.filter(t => !t.done) as todo (todo.id)}
<Todo {todo} />
{/each}
So from a developer experience perspective, this should be possible as well because inside that component the first element is technically still an immediate child of that each block.
I prepared two REPLs.
- Without child component https://svelte.dev/repl/e1c1f2459ed74d96a9de082c0be01741?version=3.46.3
- With child component, animate directive not working https://svelte.dev/repl/b4a879ed63b64d2baaebd023d2209cc0?version=3.46.3
Describe the proposed solution
I can’t fix this myself as the animate directive is resolved within the compiler I’m not so familiar with, yet.
- Proposed solutions is that the element in the component is recognized correctly as immediate child of the each block.
{#each todos.filter(t => !t.done) as todo (todo.id)}
<Todo {todo} />
{/each}
And in Todo.svelte
<script>
export let todo;
</script>
<label animate:flip>{todo.description}</label>
- Or allow the animate directive on Components instead of elements.
{#each todos.filter(t => !t.done) as todo (todo.id)}
<Todo {todo} animate:flip={{element}} bind:element/>
{/each}
And in Todo.svelte
<script>
export let todo;
export let element;
</script>
<label bind:this={element}>{todo.description}</label>
Alternatives considered
Alternatives are quite tough.
As I will also use in:receive
and out:send
crossfade transitions, I could try to identify the remaining items of the each loop by myself and put an animation on them.
Importance
would make my life easier
About this issue
- Original URL
- State: open
- Created 2 years ago
- Reactions: 47
- Comments: 17
This is a major pain point for users of Svelte Headless Table as well. The main sell is that Svelte Headless Table is truly headless so all of Svelte’s native directives should work out of the box.
However, we use a
<Subscribe/>
helper component to auto-subscribe to non-top-level Svelte stores.Without support for
$row.attrs()
in the template,<Subscribe/>
is necessary. However,animate:flip
throws an error because it’s not the direct child of the each block, even thoughSubscribe
does not add anything to the template.It would be nice if
animate:flip
is able to detect these behaviours.That’s how I fixed it but it feels a lot like a workaround to me.
This breaks the layout when using with a table (trying to animate <TableRow> components in flowbite-svelte, e.g.)
It’s 2024 and things are still not solved 😭😭😭
Error:
An element that uses the animate directive must be the immediate child of a keyed each blocksvelte(invalid-animation)
@3daddict I did not find a solution and moved away from Svelte. An issue like this tells me Svelte is not ready for my use case.
@matths I found a solution after facing the same:
“What you have is an indexed each block, which won’t work. A keyed each block looks like this. (preferably with a proper key)” Change your each block:
{#each todos.filter(t => !t.done) as todo (todo.id)}
To this:{#each todos.filter(t => !t.done) as index (todo)}
You can then use index to set an id on the element you’re creating
https://stackoverflow.com/questions/59497908/animations-in-svelte-component