2

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!

2 Answers 2

5

<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.

Sign up to request clarification or add additional context in comments.

3 Comments

Yes, I tried that, but I kept getting the error: "Render tags can only contain call expressions." Thanks to your advice, I dug deeper into this issue and found the root cause: <svelte:self> is also deprecated. After updating <svelte:self> to reference the same component, I now have a valid children value that I can use with @render.
@Thomaszter: <svelte:self> is deprecated but still works as intended, not sure how that relates to any errors. If it actually is causing errors, that should be reported.
This is correct, the app still worked. But the aim was to update the whole app to new Svelte 5 syntax.
2

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.