I'm creating a button component in Svelte that will either render as <button>
or <a>
element, depending on whether it's a link or not. Is it possible to use svelte:component
?
Something like this…
<script lang='ts'>
export let href: string = ''
$: component = href ? a : button // where "a" and "button" are the HTML DOM elements
</script>
<svelte:component this={component}>
<slot></slot>
</svelte:component>
So far, I've only seen examples of svelte:component
rendering custom Svelte components, not DOM elements
https://svelte.dev/tutorial/svelte-component
How to dynamically render components in Svelte?
It is possible to use if/else to get the desired results…
<script lang='ts'>
export let href: string = ''
</script>
{# if href}
<a {href}>
<slot></slot>
</a>
{:else}
<button>
<slot></slot>
</button>
{/if}
…but this is not maintainable.
Container
component that can be a div
, section
, article
, aside
, etc.). More variants results in an even messier code structure.Here's an example React component with the desired functionality.
const Button = (props) => {
const Tag = props.href ? 'a' : 'button'
return <Tag href={props.href}>{contents}</Tag>
}
Same pattern as above.
Instead of duplicating the children multiple times, you could move it into its own component and just import the child into the if/else chain. At least then there won't be duplicated logic, but the props/slots will need to be duplicated.
<script lang='ts'>
import Children from './children.svelte'
export let href: string = ''
</script>
{# if href}
<a {href}>
<Children><slot></slot></Children>
</a>
{:else}
<button>
<Children><slot></slot></Children>
</button>
{/if}
For every wrapper DOM element, just create a new Svelte component. Then, import those as actual Svelte components and use svelte:component
<!-- dom-a.svelte -->
<a {...$$props}><slot></slot></a>
<!-- dom-button.svelte -->
<button {...$$props}><slot></slot></button>
<!-- button.svelte -->
<script lang='ts'>
import A from './dom-a.svelte'
import Button from './dom-button.svelte'
export let href: string = ''
$: component = href ? A : Button
</script>
<svelte:component this={component}>
<slot></slot>
</svelte:component>
While this is the nicest to use as a developer, there is a performance penalty for having unknown props. Therefore, it's not idea.
I suppose you could specify every single possible prop in the dom-button.svelte
and dom-a.svelte
components, but that seems like overkill.
It is not possible to use <svelte:component>
for this. There is a proposal to add a new <svelte:element>
tag for this functionality, but it has not been implemented yet. There is an open PR to add this tag to Svelte.
EDIT: this is now possible in Svelte 3.47.0 with svelte:element. You can now do the following:
<script>
export let href = '';
let tag = href ? 'a' : 'button';
</script>
<svelte:element this={tag} {href}>
<slot></slot>
</svelte:element>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With