fareez.info

Creating a Tree Component using Svelte.js

I was playing around with Svelte.js recently and was curious to know how easy it would be to build a Tree component using it. It was simpler than I expected it to be with Svelte’s recursive svelte:self feature. Let me explain how its done.

Tree made using Svelte.js

So the idea is to have two components. One named Tree and the other one named Node. Under the hood, we will be using ul and li tags, where ul will wrap the entire tree and each li tag within it will be a node. Yes, we are going to render the tree as a flat list of li tags.

Before that let’s design the data model of the tree which has to be passed as a props to the component. Following is the type of the data.

type Node = {
    data: string,
    expanded: boolean,
    children?: Node[]
}

It is a recursive type and data fitting in the above type will look like,

let treeData = {
    data: 'Root',
    expanded: false,
    children: [
        {
            data: 'Child 1', 
            expanded: false, 
            children: [{ data: 'Grand Child 1' }]
        },
        {
            data: 'Child 2', 
            expanded: false, 
            children: [{ data: 'Grand Child 2' }]
        },
        {data: 'Child 3'}
    ]
}

Now the Tree component is just a wrapper around the root Node component.

<!-- Tree.svelte -->
<script>
	import Node from "./Node.svelte";
	export let data; // type Node
</script>

<ul>
	<Node node={data} />	
</ul>

<style>
	ul {
		list-style-type: none;
		padding: 0;
		margin: 0;
	}
</style>

Now we are reaching the important part, where we are going to render the nodes in a recursive component using svelte:self.

<!-- Node.svelte -->
<script>
    import { slide } from 'svelte/transition';
	import ClosedIcon from './ClosedIcon.svelte';
	import OpenedIcon from './OpenedIcon.svelte';
	export let node;
	export let level = 0;
	
	function toggle() {
		node.expanded = !node.expanded;
	}
</script>

<li on:click={toggle} style="padding-left:{level*1}rem" transition:slide>
	{#if !node.expanded }
		<ClosedIcon/>
	{:else}
		<OpenedIcon/>
	{/if}
	{node.data}
</li>

{#if node.expanded && node.children}
    {#each node.children as child}
        <svelte:self node={child} level={level+1}/>
    {/each}
{/if}

<style>
li {
    border-bottom: solid 1px #eee;
    margin: 0 0;
    padding: 1rem;
    background: #fafafa;
    display: flex;
}
</style>

We are rendering the node data in a <li> tag and if the list is expanded, we are recursively rendering all the children of the node right below it. Note that we have level prop and we increase it at each level. This will be used to indent the node, so that it appears like a tree.

Svelte’s built in slide transition makes it super easy to add some nice transition so that it gives a visual feedback when expanding or collapsing.

You can check this component in this Svelte REPL link.

comments powered by Disqus