Client-Side Pages, Components, and Interactivity

Template Logic and Reactivity


Learning Objectives

  • You know of Svelte’s template logic and know how to use conditional rendering.
  • You can iterate over a collection, showing information from the collection in a component.
  • You know how to combine template logic and reactive variables.
  • You know how to create a component that shows a list of items with functionality for adding, removing, and filtering the items.

Logic blocks

Svelte comes also with the possibility to add logic to the components through the use of 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 above. 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}

Conditional rendering

Conditional rendering can be used to show different content based on conditions. The block keyword for conditional rendering is if. The following outlines the basic syntax of conditional rendering.

{#if expression}
  content
{/if}

If the expression evaluates to true, the content is rendered. Otherwise, the content is not rendered.

As an example, the following component shows the text “Great choice!” if the value of the variable version is 5.

<script>
  let name = "Svelte";
  let version = 5;
</script>

<h1>Hello {name}!</h1>

{#if version === 5}
  <h1>Great choice!</h1>
{/if}
Loading Exercise...

Similar to conditional statements in programming in general, there exists also 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}

Drawing these together, the following component shows different messages based on the value of the variable version.

<script>
  let name = "Svelte";
  let version = 5;
</script>

<h1>Hello {name}!</h1>

{#if version === 5}
  <h1>You've picked the version {version}. Great choice!</h1>
{:else if version < 5}
  <h1>Your version is {version}. Time to update it!</h1>
{:else}
  <h1>Your version is {version} -- you are from the future!</h1>
{/if}

With the above, if the value of version is 5, the text “You’ve picked the version 5. Great choice!” is shown. For values smaller than 5 and greater than 5, different messages are displayed. As an example, if the value is 4, the text “Your version is 4. Time to update it!” is displayed. Similarly, if the value is 6, the text is “Your version is 6 — you are from the future!”.

Loading Exercise...

Working with objects

Objects are also variables and they can be used in the same way as other variables. As an example, we could write the above example with an object instead of two variables.

<script>
  let library = { name: "Svelte", version: 5 };
</script>

<h1>Hello {library.name}!</h1>

{#if library.version === 5}
  <h1>You've picked the version {library.version}. Great choice!</h1>
{:else if library.version < 5}
  <h1>Your version is {library.version}. Time to update it!</h1>
{:else}
  <h1>Your version is {library.version} -- you are from the future!</h1>
{/if}

The key thing to remember when working with objects is to use the dot notation to access the properties of the object — in the case of the above example, e.g. library.name. Just writing {library} would show the object itself, not e.g. the value of the property name, while writing {name} would result in an error, as the variable name is not defined.

Loading Exercise...

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. The following outlines the basic syntax of looping.

{#each list as item}
  content
{/each}

As an example, in the following, we have a variable called items that we iterate over. The items in the list are rendered into an unordered list ul, where each item is placed into a list item li.

<script>
  let items = ["item 1", "item 2", "item 3"];
</script>

<ul>
  {#each items as item}
    <li>{item}</li>
  {/each}
</ul>
Loading Exercise...

Objects and looping

As one would expect, it is possible to also loop over collections of objects. The following example shows a list of books, where each item has an identifier and a name.

<script>
  let books = [
    { id: 1, name: "HTML for Hamsters" },
    { id: 2, name: "CSS: Cannot Style Sandwiches" },
    { id: 3, name: "JavaScript and the Fifty Shades of Errors" },
  ];
</script>

<h1>Books</h1>

To render the books, we use the each block. To show the name from each book, we need to use the property name when iterating over the books. The following example shows how to create an unordered list of the books.

<script>
  let books = [
    { id: 1, name: "HTML for Hamsters" },
    { id: 2, name: "CSS: Cannot Style Sandwiches" },
    { id: 3, name: "JavaScript and the Fifty Shades of Errors" },
  ];
</script>

<h1>Books</h1>

<ul>
  {#each books as book}
    <li>{book.name}</li>
  {/each}
</ul>

Similarly, we could also use the identifier of the book in the output. Below, the identifier id is in parentheses after the book name.

<script>
  let books = [
    { id: 1, name: "HTML for Hamsters" },
    { id: 2, name: "CSS: Cannot Style Sandwiches" },
    { id: 3, name: "JavaScript and the Fifty Shades of Errors" },
  ];
</script>

<h1>Books</h1>

<ul>
  {#each books as book}
    <li>
      {book.name} ({book.id})
    </li>
  {/each}
</ul>
Loading Exercise...

Reactivity and template logic

Reactivity and template logic goes well together. The following example expands the above book list application by turning the book list into a reactive variable by wrapping it in $state(). Furthermore, the example includes a function for removing books and a button that, when clicked, calls the function.

The function for removing the books — removeBook — filters the books by id, removing one that has an identifier that corresponds to the one that is being removed.

<script>
  let books = $state([
    { id: 1, name: "HTML for Hamsters" },
    { id: 2, name: "CSS: Cannot Style Sandwiches" },
    { id: 3, name: "JavaScript and the Fifty Shades of Errors" },
  ]);

  const removeBook = (id) => {
    books = books.filter((book) => book.id != id);
  };
</script>

<h1>Books</h1>

<ul>
  {#each books as book}
    <li>
      {book.name}
      <button onclick={() => removeBook(book.id)}>Remove</button>
    </li>
  {/each}
</ul>

Notice that the event attribute is a function () => removeBook(book.id) instead of just removeBook(id).

We could also add functionality for adding books. This would require a reactive variable that is used to store the name of the book, a function for adding the book, and the corresponding HTML elements for providing the book name and for providing the event (button click) that triggers adding the book. These are included in the example below.

<script>
  let books = $state([
    { id: 1, name: "HTML for Hamsters" },
    { id: 2, name: "CSS: Cannot Style Sandwiches" },
    { id: 3, name: "JavaScript and the Fifty Shades of Errors" },
  ]);

  let bookName = $state("");

  const addBook = () => {
    const book = {
      id: books.length + 1,
      name: bookName,
    };

    books.push(book);
    bookName = "";
  };

  const removeBook = (id) => {
    books = books.filter((book) => book.id != id);
  };
</script>

<h1>Books</h1>

<ul>
  {#each books as book}
    <li>
      {book.name}
      <button onclick={() => removeBook(book.id)}>Remove</button>
    </li>
  {/each}
</ul>

<input type="text" bind:value={bookName} placeholder="New book name" />
<button onclick={addBook}>Add book</button>

There are a few challenges with the logic, including the identifiers being potentially duplicate when removing and adding books in a specific fashion. We’ll ignore this issue now, as we’ll later use a database that allows automatically assigning unique identifiers to new entries.

Loading Exercise...

As a last step in this example, let’s add functionality for filtering the list of books based on a search term. This requires a reactive variable for holding the search term and modifying the each block to filter the books based on the search term.

<script>
  let books = $state([
    { id: 1, name: "HTML for Hamsters" },
    { id: 2, name: "CSS: Cannot Style Sandwiches" },
    { id: 3, name: "JavaScript and the Fifty Shades of Errors" },
  ]);

  let bookName = $state("");
  let searchTerm = $state("");

  const addBook = () => {
    const book = {
      id: books.length + 1,
      name: bookName,
    };

    books.push(book);
    bookName = "";
  };

  const removeBook = (id) => {
    books = books.filter((book) => book.id != id);
  };
</script>

<h1>Books</h1>

<input
  type="text"
  bind:value={searchTerm}
  placeholder="Search books by name"
/>

<ul>
  {#each books.filter((book) => book.name.includes(searchTerm)) as book}
    <li>
      {book.name}
      <button onclick={() => removeBook(book.id)}>Remove</button>
    </li>
  {/each}
</ul>

<input type="text" bind:value={bookName} placeholder="New book name" />
<button onclick={addBook}>Add book</button>

Now, instead of just iterating over the books, we filter the books based on the search term. The filtering is done using the filter method of the array, which returns a new array containing only the items that match the condition — this is the array that will be finally iterated over.

The filter checks if the name of the book includes the search term using the includes method of the string.

Loading Exercise...

Summary

To summarize:

  • Template logic allows adding logic to Svelte components.
  • Conditional rendering can be used to show different content based on conditions.
  • Collections can be looped over using the each block.
  • Template logic and reactive variables go well together, allowing the creation of dynamic and interactive components.
  • Objects can be used in template logic, allowing access to their properties using the dot notation.
Loading Exercise...