Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Promises: the await block

Data from a server does not arrive at once: a request returns a promise that settles later. The {#await} block lets you describe all three states of a promise in the markup — waiting, success and error — without extra variables like isLoading.

Three branches

As an example, take the test service jsonplaceholder.typicode.com, which returns tasks as JSON:

<!-- src/routes/+page.svelte -->
<script>
  async function loadTodos() {
    const response = await fetch('https://jsonplaceholder.typicode.com/todos?_limit=5');
    if (!response.ok) throw new Error('The server responded with an error');
    return response.json();
  }
 
  let promise = $state(loadTodos());
</script>
 
{#await promise}
  <p>Loading...</p>
{:then todos}
  <ul>
    {#each todos as todo (todo.id)}
      <li>{todo.title}</li>
    {/each}
  </ul>
{:catch error}
  <p>Could not load: {error.message}</p>
{/await}

First "Loading..." flashes on the page, then a list of five tasks appears. If you turn off the internet and reload the page, you see the error message. The value in the {:then todos} branch is the result of the promise, in {:catch error} — the rejection reason.

Repeating the request

The block watches the variable holding the promise. Assign a new promise to it and the block goes from loading to the result again:

<!-- src/routes/+page.svelte: add a button below the await block -->
<button onclick={() => (promise = loadTodos())}>Refresh</button>

After the click "Loading..." appears again, followed by a fresh list. This is why the promise is kept in $state: a plain variable would not tell the block about the replacement.

Short forms

If you do not need a loading indicator, write the then branch right in the opening tag:

<script>
  let promise = $state(fetch('https://jsonplaceholder.typicode.com/todos/1').then((r) => r.json()));
</script>
 
{#await promise then todo}
  <p>First task: {todo.title}</p>
{/await}

Until the promise settles, nothing is shown in that place. Errors are not shown in this form — add if they matter.

Where to load data in SvelteKit

The {#await} block is good for requests triggered by the user: search, a "Refresh" button. Data a page needs as soon as it opens is usually fetched in SvelteKit with a load function — it has its own lesson in the SvelteKit chapter.

Common mistake

Calling the loading function right in the markup:

<script>
  async function loadTodos() {
    const response = await fetch('https://jsonplaceholder.typicode.com/todos?_limit=5');
    return response.json();
  }
</script>
 
{#await loadTodos() then todos}
  <p>Tasks: {todos.length}</p>
{/await}

This code works, but the request cannot be repeated, and every time the block is recreated it is sent again. Keep the promise in a variable and pass that variable to {#await}.

Thanks for reading!

Donate to the site — we work hard for you.