Template Logic
Learning objectives
- You know how to add logic to the template part of components.
Svelte provides the possibility to create dynamic functionality to the components through logic blocks. Logic blocks include conditional blocks, looping blocks, and blocks used to wait for completion of asynchronous functions.
Logic blocks are written using curly braces {}
, similar to JavaScript expressions. The start of a block is identified by {#
, the block keyword, an optional expression, and }
. The end of template logic is closed with {/
, the block keyword, and }
. The basic syntax is as follows.
{#block-keyword expression}
content
{/block-keyword}
Here, we look into if
, each
, and await
blocks.
Conditional rendering
Svelte provides the possibility to show optionally show content. This is done through conditional rendering, which allows creating areas that are encapsulated with an if
-statement and an associated expression. If the expression evaluates to true, the content is rendered. Otherwise, the content is not rendered.
When using conditional rendering, the block keyword is if
. The following outlines the basic syntax of conditional rendering.
{#if expression}
content
{/if}
Similar to conditional statements in programming, Svelte provides the possibility for else
and else if
branches. Both else
and else if
branches begin with a colon :
. The following outlines the basic syntax of conditional rendering with else
and else if
branches.
{#if expression}
content
{:else}
content
{/if}
{#if expression}
content
{:else if expression}
content
{:else}
content
{/if}
As a concrete example, the following component uses conditional rendering to display different content based on the age of the person.
<script>
// JavaScript goes here
let { name } = $props();
let age = $state(0);
const grow = () => {
age++;
};
</script>
<!-- HTML template goes here -->
<p>My name is {name}. I am {age} years old.</p>
{#if age >= 18}
<p>I am (legally) an adult (in Finland).</p>
{:else if age >= 4}
<p>I am a child.</p>
{:else if age >= 1}
<p>I am a toddler.</p>
{:else}
<p>I am a baby.</p>
{/if}
<button on:click={grow}>Grow</button>
<style>
/* CSS goes here */
</style>
I am 1 years old.
As curly brackets in the HTML template denote a JavaScript expression, we can also add logic to how "I am {age} years old." is written. One approach is to add an expression that uses age
to decide whether "s" should be added to the end of "year". This would be written as follows.
{age == 1? '': 's'}
With the above, the whole paragraph would be written as follows.
<p>My name is {name}. I am {age} year{age == 1? '': 's'} old.</p>
Looping over collections
Collections are looped over using the each
block, which is given the name of a variable that holds a collection of values, the keyword as
, and the name of the variable that is used for holding individual values from the collection within the loop. As an example, if we would have a variable called list
, we could create a paragraph of each item
in the list item as follows.
{#each list as item}
<p>{item}</p>
{/each}
In our component, this could look -- for example -- as follows.
<script>
// JavaScript goes here
let { name } = $props();
let age = $state(0);
let locations = ["Helsinki", "Espoo", "Vantaa"];
const grow = () => {
age++;
};
</script>
<!-- HTML template goes here -->
<p>My name is {name}. I am {age} year{age == 1 ? "" : "s"} old.</p>
{#if age >= 18}
<p>I am (legally) an adult (in Finland).</p>
{:else if age >= 4}
<p>I am a child.</p>
{:else if age >= 1}
<p>I am a toddler.</p>
{:else}
<p>I am a baby.</p>
{/if}
<button on:click={grow}>Grow</button>
<p>I have lived in:</p>
<ul>
{#each locations as location}
<li>{location}</li>
{/each}
</ul>
<style>
/* CSS goes here */
</style>
Waiting for asynchronous data
The await
block allows waiting for the execution of asynchronous functionality, such as API calls. The await
block is given an expression that is evaluated to a promise.
Within the await
block, there is a :then
block that is executed when the promise is resolved and a :catch
block that is executed when the promise is rejected. Both the :then
and :catch
blocks can include a variable -- for :then
, the variable is the value that the await
block expression returns, while for :catch
the variable contains an error if it exists.
The await
block is closed with an await
keyword. The following outlines the basic syntax of the await
block.
{#await expression}
content
{:then variable}
content
{:catch variable}
content
{/await}
The following example includes an await
block that calls an asynchronous function. The asynchronous function retrieves a joke from https://simple-joke-api.deno.dev/random
and parses the JSON response into a JavaScript object, returning it. The text "Thinking..." is shown while the joke is being retrieved and parsed. Once done, the joke is shown to the user.
<script>
// JavaScript goes here
let { name } = $props();
let age = $state(0);
let locations = ["Helsinki", "Espoo", "Vantaa"];
const fetchJoke = async () => {
const response = await fetch("https://simple-joke-api.deno.dev/random");
return await response.json();
};
const grow = () => {
age++;
};
</script>
<!-- HTML template goes here -->
<p>My name is {name}. I am {age} year{age == 1 ? "" : "s"} old.</p>
{#if age >= 18}
<p>I am (legally) an adult (in Finland).</p>
{:else if age >= 4}
<p>I am a child.</p>
{:else if age >= 1}
<p>I am a toddler.</p>
{:else}
<p>I am a baby.</p>
{/if}
<button on:click={grow}>Grow</button>
<p>I have lived in:</p>
<ul>
{#each locations as location}
<li>{location}</li>
{/each}
</ul>
<p>The best joke that I know is:</p>
{#await fetchJoke()}
<p>Thinking...</p>
{:then joke}
<p>{joke.setup} -- {joke.punchline}</p>
{/await}
<style>
/* CSS goes here */
</style>
Avoid calling...
When using the above approach, the fetchJoke
function is called when the component is rendered. In the logs, we see a message similar to the following: Avoid calling fetch
eagerly during server side rendering — put your fetch
calls inside onMount
or a load
function instead..
We'll look into onMount
and load
functions a bit later when considering the lifecycle of components. For now, we can ignore the message.