-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Proposal: dynamic elements <svelte:element>
#2324
Comments
And is it all just syntactic sugar for what's already possible? https://v3.svelte.technology/repl?version=3.0.0-beta.21&gist=42290cfe8bae4dee4df232669d12dc29 or https://svelte.dev/repl?version=3.0.0&gist=5d6c43a98bc9f6274faa0ce8ed3f915b |
That example doesn't allow for any arbitrary tag to be specified, and it would get unwieldy to support more than 2 or 3 options. |
+1! |
I just needed to do this, but I'd go probably a bit more extreme: {#each section as {depth, title, lines}}
<h{depth}>{title}</h{depth}>
{#each lines as line}
<p>{content}</p>
{/each}
{/each} To differentiate between self closing tags and tags accepting content one could do: {#each section as {tag, is_self_closing, props, content}}
{#if is_self_closing}
<{tag} {...props} />
{:else}
<{tag} {...props}>{content}</{tag}>
{/if} |
+1 Use case: Wrapper components that need to render an element (e.g. because they attach event listeners). You'd probably use a |
Copying my recent comment from chat here for posterity and so that I can link to it later: There are a few technical issues that would need to be ironed out first, mostly related to figuring out how we'll avoid doing a bunch of stuff at runtime. If this is implemented, it would probably come with some caveats. Certain tag types have to be treated as special cases when setting certain attributes on them, and we won't be able to do these checks at compile time, but we don't want to include all that logic in the compiled component |
Amen to that. I think we could even broadly caveat that by saying “do whatever element you want” but don't expect Svelte to care about following any HTML spec, etc. |
Just to clarify, this enhancement is about using a String to dynamically create a component, correct? We can already use svelte:component tag to create components on-demand from a constructor. To do this from a String instead I just create an object containing a key for each component with the value being the constructor:
Then you can create dynamically based on String later:
|
No, this is about using a string to create an element of that tag name. |
OK, for any element, even those that aren't svelte components. Got it. So a "Svelte" way to do document.createElement. |
Another real world use case for this is dynamically embedding components in content from a CMS at runtime. Here is an example using wordpress style shortcodes to embed React components in to plain html: |
I'd love another set of eyes on my implementation for this component to see if it's on target here. This is something I've been wanting for a little while so I figured I'd try my hand at it. |
Linking from here over to my latest comment on the implementation PR: #3928 (comment) |
For reference: an independent implementation -> https://github.com/timhall/svelte-elements I'd love to see this implemented in a native way. |
I have got another use case that may or may not be related. It is more generic but could handle dynamic elements names. I would like to be able generate (part of) a component tree from some other arbitrary markup. For example, I could have a component defined as JSON like: {
"component": "div",
"children": [
{
"component": "span",
"children": "dynamic components"
}
]
} that should be rendered as <div><span>dynamic components</span></div> For now, I could do something like <script>
import { onMount } from "svelte";
let container;
const comp = {
component: "div",
children: [
{
component: "span",
children: "dynamic components"
}
]
};
onMount(() => {
// a smarter function could handle many cases
const child = document.createElement(comp.component);
const subChild = document.createElement(comp.children[0].component);
subChild.textContent = comp.children[0].children;
child.appendChild(subChild);
container.appendChild(child);
});
</script>
<div bind:this={container} /> In React, this kind of stuff can be done with the My idea is, why not provide an API for manipulating the dom at compile time? This would allow us to avoid the JS payload when the 'dynamic' tree is actually never redefined. Ideally, in my opinion, the 'api for easy dom manipulation' and the 'way to do stuff only at compile time' should be two distinct features. These features would also allow us to use libraries like MDsveX on a per component basis instead of relying on the rollup bundling. -> use case: markdown from a CMS could be parsed with MDsveX on the fly. Any pointer as of how to achieve this would be greatly appreciated. :) |
My use case, which I think is a pretty obvious one, is lists. I've got a lists component that can look like this:
And I think the intent is pretty clear: the first one should render as an Svelte:element as proposed here would nicely fit my use case and avoid some otherwise pretty ugly conditions I'll need to bake into Lists.svelte. |
...or a |
Sometimes it's easier to do the ugly thing, tucked away inside an abstraction. You can make your own project-specific <script>
export let tag = 'div';
</script>
{#if tag === 'div'}
<div {...$$props}><slot/></div>
{:else if tag === 'span'}
<span {...$$props}><slot/></span>
{:else if tag === 'h1'}
<h1 {...$$props}><slot/></h1>
<!-- etc -->
{/if} Someone could implement this |
@jesseskinner I believe this has already been done, called |
@arggh True, though that one uses a slightly different approach, with a Svelte component per tag that you import and use with svelte:component. |
@jesseskinner I took that approach as a work around, I just disliked the file every time I looked at it. Also felt like it increased your bundle size just to implement something that should possibly be built in. |
I took a similar approach to port that react-library I mentioned above. I only added a subset of elements, but it still comes at a massive file size increase. It would be a huge improvement to have support for this on a framework level. |
So glad to see this PR is being actively discussed! Eagerly awaiting its arrival in the codebase! |
Sorry, for any misunderstanding I wasn't saying I disliked this proposal I was saying I loved it! I thought it would be obvious I was saying that based on just how unmaintainable the code really was but it seems there was a misunderstanding |
In the mean time, before the PR is accepted, you can use the App.svelte <script>
import Header from "./Header.svelte";
</script>
<main>
<Header headerType="h1" />
</main> Header.svelte <script>
export let headerType;
</script>
{@html `<${headerType}>Hello, World!</${headerType}>`} View on the Svelte REPL here. |
Fails on any use case that isn’t just a plain HTML element (scoped classes, bindings, actions, etc) |
any new? |
Would this allow feature allow for some random data being reactively set for web components like this for example? <script>
import { onMount } from "svelte";
import wcDefinition from "somewhere";
// changes to dynamicData would (hopefully) reflect inside our web component
// thanks to sveltes magic {dynamicData} attribute/property binding
let dynamicData = { some: ["random", "data"] };
// used to not include custom element in dom when not already defined
// to allow svelte setting dynamicData as properties instead of attributes
let wcImported = false;
onMount(async () => {
// dynamically import web component script
// this would also define the new tag / custom element
await import(wcDefinition.scriptLocation);
// include custom element im dom only when already defined
wcImported = true;
});
</script>
{#if wcImported}
<svelte:element this={wcDefinition.tag} {dynamicData}>
</svelte:element>
{/if} My use case would be to have a dynamic system of loading different types of (web) components but still having the ability to interact with them using svelte sugar. I think currently I would have to go with document.createElement() and setting data properties per hand and on every update, which feels wrong 😅 |
I think setting the type of element / tag name using something like |
I believe the main goal is to be consistent with the |
Btw, I've been wondering why not using a same Svelte component to accommodate both use cases? If a string is passed, create a native element, otherwise consider the input is a Svelte component and instantiate it. (btw I know at least React does that) That could simplify learning and using that feature, by interpreting native elements and Svelte components the same way in this context, leaving the user with a single important matter in mind: dynamically switch what's output at that point. I guess from a user standpoint it shouldn't matter whether this is a component or a native element behind. But I might me missing part of the picture, maybe a will to be able in the future to more freely customize the dynamic creation of a component or a native element, in different ways. Also, that said, it would always be possible for the end user to create their own version of this: {#if isString}
<svelte:element this={component} />
{:else}
<svelte:component this={component} />
{/if} |
In JSX we can do
Is it possible to add something as simple and straightforward as this to Svelte? My use case:
I played a bit with this example from Stephan https://svelte.dev/repl/c5c99203078e4b1587d97b6947e2d2f2?version=3.29.0 |
Linked to my question (which was funnily reacted to 😉), it could also be nice if you could do what you suggested AND also assign to |
I came here to ask the same thing, what about cases where you need to dynamically set an element OR a component? Example use-case:
In JSX land you’d just do something like const Element = isButton ? ‘button’ : Link;
return <Element /> I think svelte needs an equally terse way to accomplish this |
@madeleineostoja if svelte:element is a thing you can easily do
You'd be able to extract this to a component of course |
True, would just be nice to have that baked into a unified API, like I don't see why the Considering Svelte's API philosophy is all about reducing needless boilerplate seems like this should be a consideration? |
This would quickly turn components into readability hell, exclusive directives like |
Correct me if I'm wrong, about your specific examples:
In any case, the actual use of such dynamic components depends on what the users want to do. It's true that if they have a use case where they allow consumers of their own component to generate either a component or a native element, and on top of that they would want to support passing custom actions (with But yes, that's exactly the kind of technical aspects that I am curious about (and some other people are too). It doesn't mean it's not feasible right now, and even less that it won't be on the future. But that gives insights about why it might not be done now, or done at all. At least, it's indeed not a pure trivial decision to merge |
I played around with Stephan's example as well, but the additional wrappers weren't acceptable in my use-case (the css workaround still broke my layout pretty severely) I agree it would be nice to have clean solution baked into the language, but I realized my own simple needs are met by something as banal (though inelegant-feeling) as: NativeWrapperElement.svelte
Edit: ... I now see similar solutions have already been posted above by @jesseskinner and perhaps others as well. |
Just recording this as a requested feature, since it comes up from time to time. Not actually saying we should do it, but would be good to track the discussion.
Proposal for
<svelte:element>
What are the actual use cases? Are there any drawbacks?
The text was updated successfully, but these errors were encountered: