Svelte in Five Minutes

Share on:

Table of Contents

This is a quick guide for newcomers to Svelte who already have a background on a web framework (preferably but not necessarily Vue.js). The reader is therefore expected to have some familiarity with HTML, CSS, and JavaScript.

Minute 1: What is Svelte and Why Bother?

Svelte, on the surface, appears to be a framework like Angular, React, or Vue.js. While it shares the goal of simplifying the development of complex, interactive web applications, Svelte is not a framework runtime that runs in the web browser in the traditional sense.

Svelte, instead, is a compiler that generates standard HTML, CSS, and JavaScript (with no runtime frameworks). Svelte is:

  • Lightweight: No framework runtime library overhead
  • Fast: No virtual DOM since the DOM is manipulated directly by pure JavaScript statements
  • Easy: Gentle learning curve given its minimalistic nature

For example, a component like Hello.svelete

1<!-- Hello.svelte -->
2<script>
3 	export let message;
4</script>
5
6<b>Hello {message}</b>

is translated to something like this:

1...
2b = element("b");
3t0 = text("Hello ");
4t1 = text(/*message*/ ctx[0]);
5...

and is used like this

1<Hello message="World"/>

which results in this:

Hello World

Minute 2: What is the Anatomy of a Svelte Project?

Let’s consider a To Do application that doesn’t deviate much from the standard template produced using npx degit sveltejs/template my-svelte-project as per the official Getting Started Guide:


My To Do List


 1.
 2├── README.md                 Instructions on how to build and run the project
 3├── node_modules              Imported modules 
 4├── package.json              Dependencies
 5├── public                    Public static assets    
 6│   ├── build                 *.js and *.css Svelte-Generated CSS and JavaScript
 7│   │   ├── bundle.css
 8│   │   ├── bundle.css.map
 9│   │   ├── bundle.js
10│   │   └── bundle.js.map
11│   ├── favicon.png
12│   ├── global.css
13│   └── index.html
14├── rollup.config.js          Bundler config
15└── src                       *.svelte User-defined Svelte Components
16    ├── App.svelte            The top topmost component
17    ├── TodoList.svelte       List of items (with Delete function)
18    ├── TodoAdd.svelte        Text box to Add New Item
19    └── main.js               Top component declaration (Svelte app entry point)

Regular .svelte components have three compartments: a <script>JavaScript compartment for code</script>, the componet’s HTML, and a <style> compartment for CSS </style>, for example:

 1<!-- App.svelte -->
 2<script>
 3	import TodoList from './TodoList.svelte';
 4	import TodoAdd from './TodoAdd.svelte';
 5
 6	let myList = ["clean kitchen", "do 10,000 steps"];
 7
 8	function addItem(description) {
 9		myList = [...myList,description]
10	}
11</script>
12
13<main>
14	<div class="title">My To Do List</div>
15	<TodoList bind:todoItems={myList}/>
16	<TodoAdd on:new-item-event={({detail}) => addItem(detail)}/>
17</main>
18
19<style>
20	.title {
21		font-weight: bold;
22		font-size: 14pt;
23	}
24</style>

Minute 3: How to Set Components' Properties

A component’s property is specified using the export let property declaration as follows:

1<!-- TodoList.svelte -->
2<script>
3    export let todoItems;
4</script>
5
6<div>
7    I have <b>{todoItems.length} items</b> left to complete!
8</div>

When the component is instantiated, the variable may be passed unidirectionally as follows:

1<!-- App.svelte -->
2<script>
3    let myList = ["clean kitchen", "do 10,000 steps"];
4</script>
5
6<TodoList todoItems={myList}/>

If we want myList to be indirectly updated (via todoItems) when changed from within TodoList.svelte, then we need to create a bidirectional binding as follows:

1<!-- App.svelte -->
2<TodoList bind:todoItems={myList}/>

Important Note: Setting the internal structure of myList using the likes of push() will not cause Svelte to render the changes. The trigger for Svelte to render the change of a variable is an assignment statement. For example:

1<!-- App.svelte -->
2<script>
3    let myList = ["clean kitchen", "do 10,000 steps"];
4    function addItem(item) {
5        // myList.push(item) Don't do this 
6        myList = [...myList,item] // Do this instead!
7    }
8</script>

Minute 4: How To Make Components Talk To Their Parents

Although properties using the bind:variable can be used for components to exchange data back and forth with their parents, this is not an ideal mechanism to notify of events because that would require parent components to keep watching for changes in the variables shared with their children.

Let’s suppose that the TodoAdd.svelte component wants to notify App.svelte that the a new To Do item has been created:

1<!-- TodoAdd.svelte -->
2<script>
3    let item = ""
4    function add() {
5        // How do I tell my parent?
6    }
7</script>
8<input type="text" id="description" bind:value={item}/>
9<button type="button">Add</button>  

The first step is to assign an on:click event handler to the Add button:

1<button type="button" on:click={add}>Add</button>  

The second step is to send the item description “out” using the dispatch function:

 1<!-- TodoAdd.svelte -->
 2<script>
 3
 4    import { createEventDispatcher } from 'svelte'
 5    const dispatch = createEventDispatcher();
 6
 7    let item = ""
 8    function add() {
 9	   dispatch("new-item-event",item);
10	   item = ""
11    }
12</script>

The third step is to catch the event on the parent and so something with it, such as appending the item to the list:

1<!-- App.svelete -->
2<script>
3    let myList = ["clean kitchen", "do 10,000 steps"];
4    function addItem(event) {
5        let item = event.detail // Our item is inside here!
6        myList = [...myList,item] 
7    }
8</script>
9<TodoAdd on:new-item-event={addItem}/>

If we want to avoid the boiler plate of extracting the event.detail variable every time and have a clean function such as addItem(item) instead, we can use the following trick:

1<TodoAdd on:new-item-event={({detail}) => addItem(detail)}/>

Minute 5: How to Declare Logical Conditions

Logical conditions, like JavaScript expressions, are enclosed in { single curly braces } but use special keywords. This is an example of an If condition:

1<!-- App.svelte -->
2{#if myList.length === 0}
3    <div>All done. Great!</div>
4{:else}
5    <TodoList bind:todoItems={myList}/>
6{/if}

And this is example of traversing a list:

1<!-- TodoList.svelte -->
2<ul>
3    {#each todoItems as items, i}
4        <li> item [ <span on:click={delete(i)}>delete</span> ]</li>
5    {/each}
6</ul>

For convenience, the delete() function is provided below:

1<script>
2    export let todoItems;
3    function deleteItem(i) {
4		todoItems.splice(i,1)
5		todoItems = [...todoItems];
6	}	
7</script>