Thomaszter
Thomaszter

Reputation: 1474

How to Replace Deprecated <slot /> with {@render} in Svelte 5 for Nested Components?

I'm currently upgrading from Svelte 4 to Svelte 5 (using SvelteKit), and I'm encountering an issue when trying to replace the deprecated <slot /> with {@render} in my components.

I'm looping through a series of components that make up a list with it's nested list elements. In Svelte 4, I used <slot /> to handle nested elements. Below is a simplified example of my setup:

+page.svelte

<Component {node}>
  {#if hasChildren(node)}
    {#each node.children as child}
      <svelte:self node={child} />
    {/each}
  {/if}
</Component>

List.svelte

<script lang="ts">
    import type { List } from 'datocms-structured-text-utils';

    let { node }: { node: List } = $props();
    let { style } = $state(node);
</script>

{#if style === 'numbered'}
    <ol><slot /></ol>
{:else}
    <ul><slot /></ul>
{/if}

ListItem.svelte

<script lang="ts">
    import type { ListItem } from 'datocms-structured-text-utils';

    let { node }: { node: ListItem } = $props();
</script>

<li><slot /></li>

In Svelte 5, <slot /> has been deprecated in favor of {@render}. How can I adapt the logic I used with <slot /> to {@render} in these components?

Specifically, I’m unsure about the correct argument to use inside {@render ...} since render tags can only contain call expressions.

This is the node object

{
    "type": "list",
    "style": "bulleted",
    "children": [
        {
            "type": "listItem",
            "children": [
                {
                    "type": "paragraph",
                    "children": [
                        {
                            "type": "span",
                            "value": "This is a list"
                        }
                    ]
                }
            ]
        },
        {
            "type": "listItem",
            "children": [
                {
                    "type": "paragraph",
                    "children": [
                        {
                            "type": "span",
                            "value": "More list items"
                        }
                    ]
                }
            ]
        },
        {
            "type": "listItem",
            "children": [
                {
                    "type": "paragraph",
                    "children": [
                        {
                            "type": "span",
                            "value": "Even more"
                        }
                    ]
                }
            ]
        }
    ]
}

Any guidance on how to handle this would be appreciated!

Upvotes: 1

Views: 2298

Answers (2)

Thomaszter
Thomaszter

Reputation: 1474

Thanks to user brunnerh I dug deeper into it and found the root cause: <svelte:self> is also deprecated:

import Node from "./Node.svelte"
<Component {node}>
    {#if hasChildren(node)}
        {#each node.children as child}
            /* This is deprecated: <svelte:self node={child} /> */
            <Node node={child} />
        {/each}
    {/if}
</Component>

After updating <svelte:self /> by referencing the same component again, I have the correct export children() i needed to use {@render children()}:

<script lang="ts">
    import type { ListItem } from 'datocms-structured-text-utils';

    let { node, children }: { node: ListItem, children (): any;  } = $props();
</script>

<li>{@render children()}</li>

Thanks mates.

Upvotes: 2

brunnerh
brunnerh

Reputation: 185280

<slot /> is migrated by adding a children prop, which is implicitly created from the content passed to the component, and rendering that. E.g.

<script lang="ts">
  import { type Snippet } from 'svelte';

  let { children: Snippet } = $props();
</script>

{@render children()}

For how to migrate named slots, see this answer.

Upvotes: 3

Related Questions