Svelte conditional wrapper

I was adding an href property to the Heading.svelte component for this site and I encountered a familiar problem. If the property was present, the whole component needed to be wrapped in an <a> tag. This might lead to something like this:

1
<!-- Heading.svelte -->
2
{#if href}
3
    <a {href}>
4
        <h1><slot /></h1>
5
    </a>
6
{:else}
7
    <h1><slot /></h1>
8
{/if}

Obviously this isn't great. With React I would probably do something like this:

1
const content = <h1>{children}</h1>
2

3
if (href) {
4
    return <a href={href}>{content}</a>
5
} else {
6
    return content;
7
}

But that isn't great either. We could shorten it from 5 lines to 1 using a ternary operator but it might start to get unreadable. There's probably a more reusable React component or HOC that solves this but I haven't looked at this point.

Anyway, I found a Svelte repl with a neat idea which led to this component:

1
<!-- MaybeTag.svelte  -->
2
<script lang="ts">
3
    export let when: boolean;
4
    export let tag: string;
5
</script>
6

7
{#if when}
8
    <svelte:element this={tag} {...$$restProps}><slot /></svelte:element>
9
{:else}
10
    <slot />
11
{/if}

It uses <svelte:element> to create whatever HTML tag name is passed to MaybeTag. We also pass any extra props to this element, and use <slot> to forward the component's children. Then we can rewrite the Heading component like this:

1
<!-- Heading.svelte -->
2
<MaybeTag when={!!href} tag="a" {href}>
3
    <h1><slot /></h1>
4
</MaybeTag>

Much cleaner. We can also create a similar MaybeComponent component using <svelte:component>, but I haven't actually used this yet.

1
<!-- MaybeComponent.svelte  -->
2
<script lang="ts">
3
    export let when: boolean;
4
    export let component: ConstructorOfATypedSvelteComponent;
5
</script>
6

7
{#if when}
8
    <svelte:component this={component} {...$$restProps}><slot /></svelte:component>
9
{:else}
10
    <slot />
11
{/if}

Seemed useful enough to write it all down somewhere though. Thanks for reading :)

kckckc © 2023