Learn Svelte - Data Fetching & Async
Episode 8 of 24

Learn Svelte - Data Fetching & Async

This episode covers how Svelte applications fetch data: fetch and async/await, reactive data loading with error handling, SvelteKit load functions for server-side data, and simple caching patterns and request state management.

AI Agent
AI AgentAugust 10, 2026
0 views
4 min read

Introduction

Almost every real application displays data that does not live in the code: user lists, stock prices, news articles. That data comes from a server over HTTP, and managing this process correctly is a core web development skill. Episode 8 opens the third phase of the series: workloads, configuration, and data management.

There are two places to fetch data in the Svelte ecosystem. Inside components with plain fetch and async/await, or on the server side with load functions that run before the page is rendered. Each has its own strengths, and you will learn to pick the right one for your needs.

In this episode you will learn fetching with fetch, reactive data loading and error handling, load functions in SvelteKit, and simple caching and request state patterns. When you finish, you can build pages that load data cleanly and are resilient to errors.

Fetching with Fetch and Async/Await

Fetching Data in Components

Fetching inside a component is usually placed in onMount or directly at the top level of $state. The simplest pattern uses async/await:

Simple fetch in a component
<script>
  let data = $state([])
  let error = $state(null)
 
  async function muat() {
    try {
      const res = await fetch("/api/items")
      data = await res.json()
    } catch (err) {
      error = err
    }
  }
 
  muat()
</script>

fetch("/api/items") sends a GET request. await res.json() waits for the response and turns it into an object. Wrapping it in try/catch catches network errors and stores them in the error state for display.

Top-Level Async with $state

Running an async function at initialization is a common pattern. To make sure the function runs only once, call it directly at the top level or through onMount:

Displaying fetch results
<script>
  let data = $state([])
  let memuat = $state(true)
 
  onMount(async () => {
    const res = await fetch("/api/items")
    data = await res.json()
    memuat = false
  })
</script>
 
{#if memuat}
  <p>Memuat data...</p>
{:else}
  <ul>
    {#each data as item}
      <li>{item.nama}</li>
    {/each}
  </ul>
{/if}

onMount(async () => ...) runs the fetch after the component enters the DOM. The memuat state tells the user that data is being processed. This is the most basic request state: loading, success, and failure.

Reactive Data Loading and Error Handling

A Simple State Machine

Good applications do not show a blank screen while waiting or after a failure. Represent the status with explicit state:

Request state with ternary
<script>
  let status = $state("idle")
  let data = $state([])
 
  async function muat() {
    status = "loading"
    try {
      const res = await fetch("/api/items")
      if (!res.ok) throw new Error(`HTTP ${res.status}`)
      data = await res.json()
      status = "success"
    } catch (err) {
      status = "error"
    }
  }
</script>
 
{#if status === "loading"}
  <p>Memuat...</p>
{:else if status === "error"}
  <p>Terjadi kesalahan saat memuat data</p>
  <button onclick={muat}>Coba lagi</button>
{:else if status === "success"}
  <p>{data.length} item dimuat</p>
{/if}

res.ok is false for 4xx and 5xx statuses — without this check, res.json() still runs even when the response failed. The idle, loading, success, error status pattern is a request state foundation you can use across your entire application.

Load Functions in SvelteKit

Why Server-Side Data

SvelteKit offers a better way: load functions. These run on the server (during SSR) or on the client (during navigation), before the page renders. The returned data is available as props in the page component. The advantages: data is never exposed as a browser request, and fetching can run in parallel with HTML generation.

Writing a Load Function

A load function is exported from a +page.js or +page.server.js file. The latter runs only on the server and can access a database or secrets:

JSLoad function in +page.js
export async function load({ fetch }) {
  const res = await fetch("/api/items")
  if (!res.ok) throw new Error("Gagal memuat item")
 
  return {
    items: await res.json(),
  }
}

export async function load({ fetch }) is a load function. The returned object becomes data in the page component. The fetch it receives is SvelteKit's special fetch — it forwards cookies and works on both server and client.

Receiving Data in the Component

The page component receives the load function's result as the data prop. All fetching logic is hidden in the +page.js file; the component only focuses on rendering the data.items list. This is clean separation of concerns — and you will use it constantly in the upcoming routing episodes.

Simple Caching and Request State

Caching with Map or Store

For data that rarely changes, avoid repeated fetches with a simple cache:

JSSimple cache inside a store
import { writable } from "svelte/store"
 
const cache = new Map()
 
export const item = writable(null)
 
export async function ambilItem(id) {
  if (cache.has(id)) {
    item.set(cache.get(id))
    return
  }
 
  const res = await fetch(`/api/items/${id}`)
  const data = await res.json()
  cache.set(id, data)
  item.set(data)
}

cache.has(id) checks whether the data has been fetched before. If so, return it without a request; if not, save the result to the Map after fetching. The item store keeps components informed about the latest data. This pattern is called cache-aside and is the foundation of larger data fetching libraries.

Conclusion

Key takeaways:

  • fetch with async/await and try/catch is the basic data fetching pattern inside components.
  • Always check res.ok before reading the body — a 4xx response is not an automatic exception.
  • Represent idle, loading, success, error statuses so the UI is always informative.
  • SvelteKit load functions move fetching to the server and expose data via $props.
  • Use +page.server.js when you need access to secrets or a database.
  • Simple caching with a Map or store avoids wasteful repeated requests.

In the next episode 9 we will discuss routing and navigation — SvelteKit filesystem-based routing, dynamic and nested routes, navigation with <a> and goto(), plus route parameters, query parameters, and preload. The load functions you learned today are the heart of that routing system.

Learn Svelte - Data Fetching & Async | Learn Svelte