Skip to content

Data Fetching

React developers fetch data in useEffect (or hand it to React Query / SWR). Svelte has two approaches that map cleanly to what you already know:

  • onMount + fetch — client-side fetching; runs after the component mounts, just like useEffect with an empty dependency array
  • SvelteKit load function — in +page.ts or +page.server.ts, returns data to the page component before render; the server-side equivalent of Next.js getServerSideProps

The big win: onMount is a simple function call with no dependency array to manage, and SvelteKit’s load runs on the server so there is no client-side loading flash for initial data.

React
function PostList() {
const [posts, setPosts] = React.useState([]);
const [loading, setLoading] = React.useState(true);
const [error, setError] = React.useState(null);
React.useEffect(() => {
let cancelled = false;
fetch('/api/posts')
.then(r => r.json())
.then(data => {
if (!cancelled) {
setPosts(data);
setLoading(false);
}
})
.catch(err => {
if (!cancelled) {
setError(err.message);
setLoading(false);
}
});
return () => { cancelled = true; };
}, []);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return <ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>;
}
Svelte
<script>
import { onMount } from 'svelte';
let posts = $state([]);
let loading = $state(true);
let error = $state(null);
onMount(async () => {
try {
const res = await fetch('/api/posts');
posts = await res.json();
} catch (e) {
error = e.message;
} finally {
loading = false;
}
});
</script>
{#if loading}
<p>Loading...</p>
{:else if error}
<p>Error: {error}</p>
{:else}
<ul>
{#each posts as post}
<li>{post.title}</li>
{/each}
</ul>
{/if}

Note: onMount runs once after mount. For one-shot fetches you do not need a cancellation flag — the component is already mounted. If you navigate away before the fetch resolves, reassigning $state after destruction is a no-op. Run this pattern in a SvelteKit project.

React
// Next.js pages/posts.tsx
export async function getServerSideProps(context) {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();
return { props: { posts } };
}
export default function Posts({ posts }) {
return (
<ul>
{posts.map(p => <li key={p.id}>{p.title}</li>)}
</ul>
);
}
Svelte
// +page.server.ts
export async function load({ fetch }) {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();
return { posts };
}
// +page.svelte — data is injected automatically
<script>
let { data } = $props();
</script>
<ul>
{#each data.posts as post}
<li>{post.title}</li>
{/each}
</ul>

SvelteKit’s load function receives an enhanced fetch that works on both server and client, handles cookies, and is automatically available — no import needed. Run this pattern in a SvelteKit project.

Live network requests are unreliable in the sandbox. This demo simulates a 600 ms API call with setTimeout — the same $state + onMount pattern you’d use with real fetch.

When does onMount run in Svelte?
Where does a SvelteKit load function run when placed in +page.server.ts?
How does a Svelte component receive data returned by a SvelteKit load function?