You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Snippets are great, but have are not flexible/powerful enough.
The exising workarounds suck.
Snippets and Components are not interchangable, but should be.
Composability of Snippets/Components in Svelte is limited.
Motivation
Until now, Components have been the "backbone" of Svelte. Usually, nothing works without them.
Snippets add more flexibility and replace the old slots.
Snippets are great. They allow for a kind of composability, mainly the ability to pass "snippets" of UI to another Component.
It also works great for sharing code for UI-parts that are "not worth creating a component file".
This is great for the end-user and also for library authors that implement a View/Component that should show some arbitrary UI in some place within.
This only works "flawlessly" inside the template. When using Snippets in (JS) code, then there are severe limitations!
Works great in Template
<scriptlang="ts">
importContainerfrom"./Container.svelte"const a =1const b =2
</script>
<Container>
{#snippetchildren()}
<p>a = {a}</p>
<p>b = {b}</p>
{/snippet}
</Container>
Limitations in Code
<scriptlang="ts">
importModalStackfrom"./ModalStack.svelte"let modalStack:ModalStack// a snippet where everyting is populated inside the template worksmodalStack.pushModal(specificModalContent)// ❌ not possible to populate a snippet within codemodalStack.pushModal(genericWarning("Some runtime reson"))modalStack.pushModal(() =>genericWarning("Some runtime reson"))
</script>
<ModalStackbind:this={modalStack} />
{#snippetspecificModalContent()}
<pclass="text-xl text-red">RAM Outtage Warning</p>
{/snippet}
{#snippetgenericWarning(text: string)}
<pclass="text-xl text-orange">{text}</p>
{/snippet}
Authors can work around this, in places they accept a Snippet, by accepting not only a Snippet, but also its props/arguments.
But this is not only cumbersome, but also hard to set TypeScript types correctly.
This solves our problem, but now makes using Snippets without arguments worse.
But there is another problem: When you want to use a Component in a place only Snippets are allowed in, you have to use redirection.
Using Components as a Snippet
<scriptlang="ts">
importModalStackfrom"./ModalStack.svelte"importComponentModalfrom"./ComponentModal.svelte"let modalStack:ModalStack// ❌ does not workmodalStack.pushModal(ComponentModal)// we can work around this too, by "redirecting" to the populated snippet defined belowmodalStack.pushModal(snippetComponentModal)
</script>
<ModalStackbind:this={modalStack} />
{#snippetsnippetComponentModal()}
<ComponentModal />
{/snippet}
But now imagine if this Component had props! Just like Snippets can have props/arguments.
The Problem I See
As a library author I would like to support all "kinds of UI" a user wants to give to me.
The user should be able to provide any view they choose:
Why do we have to do this?
Because to render it, you have to know what it is:
Rendering such a Modal
<scriptlang="ts">
let { modal }: { modal:Modal<unknown> } =$props()
</script>
{#if"component"inmodal&&"props"inmodal}
<modal.component {...modal.props} />
{:elseif"component"inmodal}
<modal.component />
{:elseif"snippet"inmodal&&"arguments"inmodal}
{@rendermodal.snippet(...modal.arguments)}
<!-- For the record: The above isn't even possible, because you cannot spread an array into a Snippet --><!-- Therefore you'd have to have it be `Snippet<[T]>`, and you can only pass _one_ value -->
{:elseif"snippet"inmodal}
{@rendermodal.snippet()}
{/if}
Some of the problem with the above are:
This is unsustainable especially when it comes to typing
The logic of what to render should be passed by the user, and not be managed by a library
This code should ideally not even be able to access arguments/props! They should be isolated from this code.
Why this is a Problem
Think about this, you want to render a list of Snippets inside buttons and highlight the last one clicked on:
<scriptlang="ts">
import { typeIconType, gear, house } from"./icons"// we have to "redirect" to the "populated" snippets defined belowsnippets.push(gearIcon)snippets.push(houseIcon)
</script>
{#snippetgenericIcon(icon: IconType, selected: boolean)}
<Icon {icon} color={selected?"blue":"neutral"} />
{/snippet}
{#snippetgearIcon(selected: boolean)}
{@rendergenericIcon(gear, selected)}
{/snippet}
{#snippethouseIcon(selected: boolean)}
{@rendergenericIcon(house, selected)}
{/snippet}
Because you can not fix/set/populate Snippet arguments (neither by parts, or all), you have to defined snippets for each entry seperately below.
How this could/should look
<scriptlang="ts">
import { typeIconType, gear, house } from"./icons"// this converts the `Snippet<[boolean, IconType]>` into `Snippet<[boolean]>`, prepopulating the iconsnippets.push((selected) =>genericIcon(gear, selected))snippets.push((selected) =>genericIcon(house, selected))
</script>
{#snippetgenericIcon(icon: IconType, selected: boolean)}
<Icon {icon} color={selected?"blue":"neutral"} />
{/snippet}
Having the ability to populate/bind Snippet arguments, while also keeping others free to "pass on" greatly improves Composability.
Composability is a design principle where complex systems are built from independent, interchangeable components (like LEGOs) that can be easily assembled, rearranged, or replaced to create new functions or adapt to changes, offering flexibility, reuse, and agility, especially in software, IT, and business architecture. It allows building specialized solutions by combining existing blocks, reducing development from scratch and enabling quicker responses to new needs.
Let's go even further
But I don't think we should stop there.
When a user defined the above genericIcon not as a Snippet, but as a Component, then they would have to "redirect using a Snippet" again!
They should not be punished for using a Component if they prefer that. Therefore the below code should be available to them too:
Now this would solve all of the above issues/limitations/problems at once, plus it would improve/add composability.
Which, to my belief, is highly advisable.
With the above implemented, even the following code should be possible:
<scriptlang="ts">
// even this should be possible:const genericIcon = (icon, selected) =>Icon({ icon, color: selected?"blue":"neutral" })snippets.push((selected) =>genericIcon(gear, selected))snippets.push((selected) =>genericIcon(house, selected))// and also this should be possible:const genericIconFactory = (icon) => {return (selected) =>Icon({ icon, color: selected?"blue":"neutral" }) }snippets.push(genericIcon(gear))snippets.push(genericIcon(house))
</script>
Conclusion
I believe in a future where authors are more powerful using Svelte than ever, having the ability to compose UI in a more advanced and flexible way.
I love the way Svelte looks, works and behaves, its simplicity is important to us all.
But being powerful, dynamic and flexible is important too.
That's why many reach for other frameworks, like React or now even Ripple. I think Svelte should provide equal "power" to the user to compose UI, and not "stand in the way" and require the user to use these cumbersome workarounds (see above).
Dream big
'Choose the right tool for the job' is sensible but boring advice.
It makes us small in our ambitions. I want us to dream bigger. I don't want to feel like my tools can't handle evolving requirements, or > that if I want to dabble in a new field I need to learn an entirely new way of working first.
Even if it turns out to be unachievable, I find it valuable to ask the question 'what would it take for SvelteKit to be the best framework for any app?', whether it's purely static content, or a realtime multiplayer app, or an offline-first productivity app, or even something built for an augmented reality headset.
I have used Svelte for 3+ years to make GroupTube. A website that is also packaged as a mobile app.
I've created multiple "View Controllers" for "Modals", "Tabs" and similar kinds of interfaces. I ran into the limitations discussed above many times during this.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Svelte Advanced Component+Snippet Proposal
This proposal outlines behavior and syntax for a combination of Components and Snippets in Svelte.
TL;DR
Motivation
Until now, Components have been the "backbone" of Svelte. Usually, nothing works without them.
Snippets add more flexibility and replace the old slots.
Snippets are great. They allow for a kind of composability, mainly the ability to pass "snippets" of UI to another Component.
It also works great for sharing code for UI-parts that are "not worth creating a component file".
This works great with Snippets
Sharing Code by exporting a Snippet
This is great for the end-user and also for library authors that implement a View/Component that should show some arbitrary UI in some place within.
This only works "flawlessly" inside the template. When using Snippets in (JS) code, then there are severe limitations!
Works great in Template
Limitations in Code
Authors can work around this, in places they accept a Snippet, by accepting not only a Snippet, but also its props/arguments.
But this is not only cumbersome, but also hard to set TypeScript types correctly.
Possible Workaround
This was first discussed in #10678.
This solves our problem, but now makes using Snippets without arguments worse.
But there is another problem: When you want to use a Component in a place only Snippets are allowed in, you have to use redirection.
Using Components as a Snippet
But now imagine if this Component had props! Just like Snippets can have props/arguments.
The Problem I See
As a library author I would like to support all "kinds of UI" a user wants to give to me.
The user should be able to provide any view they choose:
Internallsyyou have to treat a "Modal" similar to this:
Why do we have to do this?
Because to render it, you have to know what it is:
Rendering such a Modal
Some of the problem with the above are:
Why this is a Problem
Think about this, you want to render a list of Snippets inside buttons and highlight the last one clicked on:
Now a user of this code wants to add a Snippet, this case works fine:
But this turns into a bad DX really fast:
Because you can not fix/set/populate Snippet arguments (neither by parts, or all), you have to defined snippets for each entry seperately below.
How this could/should look
Having the ability to populate/bind Snippet arguments, while also keeping others free to "pass on" greatly improves Composability.
Let's go even further
But I don't think we should stop there.
When a user defined the above
genericIconnot as a Snippet, but as a Component, then they would have to "redirect using a Snippet" again!They should not be punished for using a Component if they prefer that. Therefore the below code should be available to them too:
Now this would solve all of the above issues/limitations/problems at once, plus it would improve/add composability.
Which, to my belief, is highly advisable.
With the above implemented, even the following code should be possible:
Conclusion
I believe in a future where authors are more powerful using Svelte than ever, having the ability to compose UI in a more advanced and flexible way.
I love the way Svelte looks, works and behaves, its simplicity is important to us all.
But being powerful, dynamic and flexible is important too.
That's why many reach for other frameworks, like React or now even Ripple. I think Svelte should provide equal "power" to the user to compose UI, and not "stand in the way" and require the user to use these cumbersome workarounds (see above).
Who is this?
I have used Svelte for 3+ years to make GroupTube. A website that is also packaged as a mobile app.
I've created multiple "View Controllers" for "Modals", "Tabs" and similar kinds of interfaces. I ran into the limitations discussed above many times during this.
(See comments in #10678 for some of it.)
Beta Was this translation helpful? Give feedback.
All reactions