Skip to main content
Fetch and cache data with the createQuery function. It returns a reactive query result that automatically updates when dependencies change in Svelte 5’s runes mode.

Signature

function createQuery<TQueryFnData, TError, TData, TQueryKey>(
  options: Accessor<CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>>,
  queryClient?: Accessor<QueryClient>,
): CreateQueryResult<TData, TError>

Parameters

options
Accessor<CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>>
required
A function (accessor) returning query configuration options.
queryClient
Accessor<QueryClient>
Accessor returning a custom QueryClient instance. If not provided, uses the client from context.

Returns

CreateQueryResult<TData, TError>
object
Reactive query state accessible via Svelte runes.

Type Parameters

  • TQueryFnData - Type returned by the query function
  • TError - Type of error (defaults to DefaultError)
  • TData - Type of data returned (defaults to TQueryFnData)
  • TQueryKey - Type of the query key (defaults to QueryKey)

Examples

Basic Usage (Svelte 5 Runes)

<script lang="ts">
import { createQuery } from '@tanstack/svelte-query'

const query = createQuery(() => ({
  queryKey: ['todos'],
  queryFn: async () => {
    const res = await fetch('/api/todos')
    return res.json()
  },
}))
</script>

{#if query.isLoading}
  <div>Loading...</div>
{:else if query.error}
  <div>Error: {query.error.message}</div>
{:else if query.data}
  <ul>
    {#each query.data as todo}
      <li>{todo.title}</li>
    {/each}
  </ul>
{/if}

Reactive Query Keys

<script lang="ts">
import { createQuery } from '@tanstack/svelte-query'

let todoId = $state(1)

// Query automatically refetches when todoId changes
const query = createQuery(() => ({
  queryKey: ['todo', todoId],
  queryFn: async () => {
    const res = await fetch(`/api/todos/${todoId}`)
    return res.json()
  },
}))

function nextTodo() {
  todoId++
}
</script>

<button onclick={nextTodo}>Next Todo</button>

{#if query.data}
  <h2>{query.data.title}</h2>
{/if}

With TypeScript

<script lang="ts">
import { createQuery } from '@tanstack/svelte-query'

interface Todo {
  id: number
  title: string
  completed: boolean
}

const query = createQuery(() => ({
  queryKey: ['todos'],
  queryFn: async (): Promise<Todo[]> => {
    const res = await fetch('/api/todos')
    return res.json()
  },
}))

// query.data is typed as Todo[] | undefined
</script>

Conditional Fetching

<script lang="ts">
import { createQuery } from '@tanstack/svelte-query'

let userId = $state<number | null>(null)

const query = createQuery(() => ({
  queryKey: ['user', userId],
  queryFn: () => fetchUser(userId!),
  enabled: userId !== null, // Only fetch when userId is set
}))
</script>

With Select

<script lang="ts">
import { createQuery } from '@tanstack/svelte-query'

const query = createQuery(() => ({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  select: (todos) => todos.filter(todo => !todo.completed),
}))

// query.data only contains incomplete todos
</script>

Dependent Queries

<script lang="ts">
import { createQuery } from '@tanstack/svelte-query'

// First query
const userQuery = createQuery(() => ({
  queryKey: ['user'],
  queryFn: fetchUser,
}))

// Second query depends on first
const projectsQuery = createQuery(() => ({
  queryKey: ['projects', userQuery.data?.id],
  queryFn: () => fetchProjects(userQuery.data!.id),
  enabled: !!userQuery.data?.id,
}))
</script>

Initial Data

<script lang="ts">
import { createQuery } from '@tanstack/svelte-query'

interface Props {
  todoId: number
  initialTodo?: Todo
}

let { todoId, initialTodo }: Props = $props()

const query = createQuery(() => ({
  queryKey: ['todo', todoId],
  queryFn: () => fetchTodo(todoId),
  initialData: initialTodo,
}))
</script>

Polling/Refetch Interval

<script lang="ts">
import { createQuery } from '@tanstack/svelte-query'

const query = createQuery(() => ({
  queryKey: ['realtime-data'],
  queryFn: fetchRealtimeData,
  refetchInterval: 5000, // Refetch every 5 seconds
}))
</script>

Manual Refetch

<script lang="ts">
import { createQuery } from '@tanstack/svelte-query'

const query = createQuery(() => ({
  queryKey: ['data'],
  queryFn: fetchData,
}))

function refresh() {
  query.refetch()
}
</script>

<button onclick={refresh}>Refresh Data</button>

{#if query.data}
  <pre>{JSON.stringify(query.data, null, 2)}</pre>
{/if}

Error Handling

<script lang="ts">
import { createQuery } from '@tanstack/svelte-query'

const query = createQuery(() => ({
  queryKey: ['data'],
  queryFn: fetchData,
  retry: 3,
  retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
}))
</script>

{#if query.isError}
  <div>
    <p>Error: {query.error?.message}</p>
    <button onclick={() => query.refetch()}>Try Again</button>
  </div>
{:else}
  <!-- Success content -->
{/if}

Multiple Reactive Dependencies

<script lang="ts">
import { createQuery } from '@tanstack/svelte-query'

let filter = $state('all')
let page = $state(1)

// Query refetches when either filter or page changes
const query = createQuery(() => ({
  queryKey: ['todos', filter, page],
  queryFn: async () => {
    const res = await fetch(`/api/todos?filter=${filter}&page=${page}`)
    return res.json()
  },
}))
</script>

<select bind:value={filter}>
  <option value="all">All</option>
  <option value="active">Active</option>
  <option value="completed">Completed</option>
</select>

<button onclick={() => page--} disabled={page === 1}>Previous</button>
<button onclick={() => page++}>Next</button>

With Loading States

<script lang="ts">
import { createQuery } from '@tanstack/svelte-query'

const query = createQuery(() => ({
  queryKey: ['todos'],
  queryFn: fetchTodos,
}))
</script>

{#if query.isLoading}
  <div class="spinner">Loading...</div>
{:else if query.isError}
  <div class="error">
    <h3>Error occurred</h3>
    <p>{query.error.message}</p>
  </div>
{:else if query.isSuccess}
  <div class="success">
    <!-- Render data -->
  </div>
{/if}

Notes

Svelte Query uses Svelte 5’s runes mode. The options parameter must be an accessor (function) to track reactive dependencies.
Access query state properties directly (e.g., query.data, query.isLoading) without needing to use $state or stores.
Make sure to use Svelte 5 with runes mode enabled. Svelte Query v5+ requires Svelte 5.

Build docs developers (and LLMs) love