Skip to content

Recursive Components

ListItem:

vue
<script lang="ts" setup>
type Item = { name: string; sub?: Item[] };

defineProps<{
  item?: Item;
}>();
</script>

<template>
  <li v-if="item?.sub?.length">
    {{ item.name }}

    <ul>
      <ListItem v-for="(subItem, subIndex) in item.sub" :key="subIndex" :item="subItem" />
    </ul>
  </li>

  <li v-else>{{ item?.name }}</li>
</template>
svelte
<script lang="ts">
  type Item = { name: string; sub?: Item[] };

  export let item: Item;
</script>

{#if item?.sub?.length}
  <li>
    {item.name}

    <ul>
      {#each item.sub as subItem, subIndex (subIndex)}
        <svelte:self item={subItem} />
      {/each}
    </ul>
  </li>
{:else}
  <li>{item.name}</li>
{/if}
tsx
type Item = { name: string; sub?: Item[] };

interface Props {
  item?: Item;
}

export function ListItem(props: Props) {
  const { item } = props;

  if (item?.sub?.length) {
    return (
      <li>
        {item.name}

        <ul>
          {item.sub.map((subItem, subIndex) => (
            <ListItem key={subIndex} item={subItem} />
          ))}
        </ul>
      </li>
    );
  }

  return <li>{item.name}</li>;
}

Usage:

vue
<script lang="ts" setup>
import ListItem from '~/components/ListItem.vue';

const list = [
  { name: 'A' },
  { name: 'B', sub: [{ name: 'B-1' }] },
  { name: 'C', sub: [{ name: 'C-1', sub: [{ name: 'C-1-1' }] }] },
];
</script>

<template>
  <ul>
    <ListItem v-for="(item, index) in list" :key="index" :item="item" />
  </ul>
</template>
svelte
<script lang="ts">
  import ListItem from '$lib/components/ListItem.svelte';

  const list = [
    { name: 'A' },
    { name: 'B', sub: [{ name: 'B-1' }] },
    { name: 'C', sub: [{ name: 'C-1', sub: [{ name: 'C-1-1' }] }] },
  ];
</script>

<ul>
  {#each list as item, index (index)}
    <ListItem {item} />
  {/each}
</ul>
tsx
import { ListItem } from '~/components/ListItem';

export function App() {
  const list = [
    { name: 'A' },
    { name: 'B', sub: [{ name: 'B-1' }] },
    { name: 'C', sub: [{ name: 'C-1', sub: [{ name: 'C-1-1' }] }] },
  ];

  return (
    <ul>
      {list.map((item, index) => (
        <ListItem key={index} item={item} />
      ))}
    </ul>
  );
}

Released under the MIT License.