-
-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Svelte 5: Support passing of snippets or components? #9774
Comments
I'm wondering how common this really is that it requires an ergonomic shortcut. Snippets are strictly inferior to components, so one could just do this <List {items}>
{#snippet item(data)}
<ListItem {...data} />
{/snippet}
</List> |
I agree. This feels like an additional thing to do at runtime (affecting everyone's components in all apps) for the sake of a weird use case, when the alternative is to just write a little bit more of more-explicit code. |
It’s only an additional thing to do if snippets and components work differently, but currently they are pretty similar under the hood. |
Another compromise would be a utility function for converting a component to a snippet so you could do something like: <List {items} item={asSnippet(ListItem)} /> This should not affect anything else. (Tried to make that work in user land but could not quite manage to do so.) |
The key word here is 'currently'. Things will doubtless evolve in future (#9672 is an example of a change we might make, albeit one that probably wouldn't affect this sort of interoperability), and it's very likely that we'd come to regret doing this. That's enough of a reason for me to oppose this proposal, but beyond that I don't think it's desirable anyway — I've often regretted these kinds of loosey-goosey APIs. Prefer clarity and consistency over convenience, in almost all cases. |
Totally fair to want to preserve the ability to iterate. But over time I wouldn't be surprised if they naturally converged. I would argue that if In my head, the difference between a Component and a snippet is pretty similar to the difference between a function defined at the top-level and one defined in some nested scope. Here's my REPL with a component and snippet side-by-side. Looking at the compiled output, it looks like the only difference is the way args are passed, and I would say it kinda looks like the snippet could actually benefit from being treated more like the And I have to go here because it's the natural next question: do you need {#snippet MySnippet({ arg1, arg2 })}
<div class={arg1}>{arg2}</div>
{/snippet}
<MySnippet arg1="foo" arg2="Bar" /> |
To be clear: if the only way to implement this proposal is to have code that looks like this: return isSnippet(thing) ? renderSnippet(thing) : renderComponent(thing) then please don't do it. It works only if they're naturally interoperable. That is a loosey-goosey API. |
In #9903 it was asked to not only differentiate between snippets and components, but also between those two and regular functions - i.e. |
{#snippet actionsDom()} can snippet support this syntax? |
For the purposes of my component mocking tool, it would be great to be able to pass in a component as the argument for a snippet prop, because the props are written in Typescript. If
Then I could build up a variant like this: import MockAvatar from './MockAvatar.svelte'
export const myVariant = {
name: "Bob",
image: asSnippet(MockAvatar),
} |
* feat: provide isSnippet type, deduplicate children prop from default slot fixes #10790 part of #9774 * fix ce bug * remove isSnippet type, adjust test * fix types * revert unrelated changes * remove changeset * enhance test * fix * fix * fix * fix, different approach without needing symbol --------- Co-authored-by: Rich Harris <rich.harris@vercel.com>
I would love to have {#if isSnippet(item)}
{@render item(args)}
{:else if isComponent(item)}
<svelte:component this={item} {...args} />
{/if} As an example I currently have a component which is an abstraction similar to the <script>
let { as, children, ...props } = $props();
</script>
{#if typeof as === 'function'}
{@render as(props, children)}
{:else if typeof as === 'string'}
<svelte:element this={as}>{@render children?.()}</svelte:element>
{:else}
{@render children?.()}
{/if} Being able to turn the above into something like this would be amazing: <script>
let { as, children, ...props } = $props();
</script>
{#if isSnippet(as)}
{@render as(props, children)}
{:else if isComponent(as)}
<svelte:component this={as} {...props}>{@render children?.()}</svelte:component>
{:else if typeof as === 'string'}
<svelte:element this={as}>{@render children?.()}</svelte:element>
{:else}
{@render children?.()}
{/if} |
+1 for an isSnippet / isComponent function to support more flexible reusable components. With filename disappearing, I started looking at function names and even tempted by scanning fn.toString but of course that's brittle.... appreciate advice on a less brittle workaround if this won't make the v5 cut. Cheers. |
Here's a simple work around I've found. type Props = {
icon: SvelteComponent | Snippet
};
let {
icon = ChevronDown
}: Props = $props();
<button class="dropdown-title" {onclick}>
{#if icon && icon.length === 1}
{@render icon()}
{:else if icon}
<svelte:component this={icon} class="w-4" />
{/if}
<span>{title}</span>
</button>
|
Describe the problem
This came up in Discord:
It might be useful for library components to accept either a snippet or a regular component as input.
Describe the proposed solution
There would have to be a way to either:
@render
accept components.The first argument (in case Svelte 5: Variadic snippets #9672 is implemented) to the called function would then be considered the props in case of a component.
Alternatives considered
Always require the use of snippets, which can just use the desired component internally.
Just a bit more verbose.
Importance
nice to have
The text was updated successfully, but these errors were encountered: