Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
ee9346a
WIP: Svelte 5 adapter (#6981)
zhihengGet Jul 26, 2024
b5f5fd4
feat(svelte-query): Improve svelte runes API (#8852)
elliott-with-the-longest-name-on-github May 12, 2025
594e0bf
Merge branch 'main' into svelte-5-adapter
lachlancollins May 12, 2025
e3f7484
Use sleep from query-test-utils
lachlancollins May 12, 2025
859d9b7
Simplify test reset logic
lachlancollins May 12, 2025
746b9c5
Merge branch 'main' into svelte-5-adapter
lachlancollins May 12, 2025
6e65a4a
Merge branch 'main' into svelte-5-adapter
lachlancollins Sep 26, 2025
540ce06
Fix some merge conflicts
lachlancollins Sep 26, 2025
f0a9ebe
More fixes
lachlancollins Sep 26, 2025
cc5a571
A few more fixes
lachlancollins Sep 26, 2025
d484552
Merge branch 'main' into svelte-5-adapter
lachlancollins Sep 26, 2025
686db47
Fix useMutationState
lachlancollins Sep 26, 2025
e3bba4a
Add changeset
lachlancollins Sep 26, 2025
373e24b
Add migration docs
lachlancollins Sep 26, 2025
d0beb44
Merge branch 'main' into svelte-5-adapter
lachlancollins Sep 27, 2025
bd8737f
Replace Set with SvelteSet
lachlancollins Sep 27, 2025
e07e895
Update minimum svelte version
lachlancollins Sep 27, 2025
9f5c7e8
Bump svelte-eslint-parser
lachlancollins Sep 27, 2025
eea078e
Unwrap createQuery test
lachlancollins Sep 27, 2025
968d6a8
fix(svelte-query): `state_unsafe_mutation` error with `useIs...` (#9493)
hmnd Sep 27, 2025
62be7d5
chore(svelte-query): fix eslint config (#9699)
lachlancollins Sep 27, 2025
c1e67ed
Merge branch 'main' into svelte-5-adapter
lachlancollins Sep 27, 2025
c6d0b92
Merge branch 'main' into svelte-5-adapter
lachlancollins Sep 30, 2025
05f0acd
ci: apply automated fixes
autofix-ci[bot] Sep 30, 2025
7049e4b
Fix sherif
lachlancollins Sep 30, 2025
0c44073
Update docs and changeset
lachlancollins Sep 30, 2025
f3fc6a2
Update keywords
lachlancollins Sep 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/pink-pots-jam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@tanstack/svelte-query-persist-client': major
'@tanstack/svelte-query-devtools': major
'@tanstack/svelte-query': major
---

BREAKING: Migrate to svelte runes (svelte v5+). Please see documentation for migration guide.
4 changes: 2 additions & 2 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@
"to": "framework/svelte/ssr"
},
{
"label": "Reactivity",
"to": "framework/svelte/reactivity"
"label": "Migrate from v5 to v6",
"to": "framework/svelte/migrate-from-v5-to-v6"
}
]
},
Expand Down
2 changes: 0 additions & 2 deletions docs/framework/svelte/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ title: Installation

You can install Svelte Query via [NPM](https://npmjs.com).

> v5 is currently available as a release-candidate. We don't anticipate any major API changes from here on out. We encourage you to try it out and report any issues you find.
### NPM

```bash
Expand Down
60 changes: 60 additions & 0 deletions docs/framework/svelte/migrate-from-v5-to-v6.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
## Overview

While Svelte v5 has legacy compatibility with the stores syntax from Svelte v3/v4, it has been somewhat buggy and unreliable for this TanStack Query adapter. The `@tanstack/svelte-query` v6 adapter fully migrates to runes syntax.

## Installation

Please ensure your project has [Svelte v5.25.0](https://github.com/sveltejs/svelte/releases/tag/svelte%405.25.0) or newer.

Run `pnpm add @tanstack/svelte-query@latest` (or your package manager's equivalent).

> Note that `@tanstack/svelte-query` v6 depends on `@tanstack/query-core` v5.

## Thunks

Like the Solid adapter, most functions for the Svelte adapter now require options to be provided as a "thunk" (`() => options`) to maintain reactivity.

```diff
-const query = createQuery({
+const query = createQuery(() => ({
queryKey: ['todos'],
queryFn: () => fetchTodos(),
-})
+}))
```

## Accessing Properties

Given the adapter no longer uses stores, it is no longer necessary to prefix with `$`.

```diff
-{#if $todos.isSuccess}
+{#if todos.isSuccess}
<ul>
- {#each $todos.data.items as item}
+ {#each todos.data.items as item}
<li>{item}</li>
{/each}
</ul>
{/if}
```

## Disabling Legacy Mode

If your component has any stores, it might not properly switch to runes mode. You can ensure your application is using runes in two ways:

### On a per-file basis

In each `.svelte` file, once you have migrated to runes, add `<svelte:options runes={true} />`. This is better for large applications requiring gradual migration.

### On an project-wide basis

In your `svelte.config.js`, add the following to config:

```json
compilerOptions: {
runes: true,
},
```

This can be added once you've 100% eradicated stores syntax from your app.
21 changes: 12 additions & 9 deletions docs/framework/svelte/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ title: Overview

The `@tanstack/svelte-query` package offers a 1st-class API for using TanStack Query via Svelte.

> Migrating from stores to the runes syntax? See the [migration guide](../migrate-from-v5-to-v6).

## Example

Include the QueryClientProvider near the root of your project:
Expand All @@ -28,19 +30,19 @@ Then call any function (e.g. createQuery) from any component:
<script lang="ts">
import { createQuery } from '@tanstack/svelte-query'

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

<div>
{#if $query.isLoading}
{#if query.isLoading}
<p>Loading...</p>
{:else if $query.isError}
<p>Error: {$query.error.message}</p>
{:else if $query.isSuccess}
{#each $query.data as todo}
{:else if query.isError}
<p>Error: {query.error.message}</p>
{:else if query.isSuccess}
{#each query.data as todo}
<p>{todo.title}</p>
{/each}
{/if}
Expand All @@ -62,6 +64,8 @@ Svelte Query offers useful functions and components that will make managing serv
- `useQueryClient`
- `useIsFetching`
- `useIsMutating`
- `useMutationState`
- `useIsRestoring`
- `useHydrate`
- `<QueryClientProvider>`
- `<HydrationBoundary>`
Expand All @@ -70,5 +74,4 @@ Svelte Query offers useful functions and components that will make managing serv

Svelte Query offers an API similar to React Query, but there are some key differences to be mindful of.

- Many of the functions in Svelte Query return a Svelte store. To access values on these stores reactively, you need to prefix the store with a `$`. You can learn more about Svelte stores [here](https://learn.svelte.dev/tutorial/writable-stores).
- If your query or mutation depends on variables, you must use a store for the options. You can read more about this [here](../reactivity).
- The arguments to the `create*` functions must be wrapped in a function to preserve reactivity.
45 changes: 0 additions & 45 deletions docs/framework/svelte/reactivity.md

This file was deleted.

8 changes: 4 additions & 4 deletions docs/framework/svelte/ssr.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ export async function load() {
export let data: PageData
const query = createQuery({
const query = createQuery(() => ({
queryKey: ['posts'],
queryFn: getPosts,
initialData: data.posts,
})
}))
</script>
```

Expand Down Expand Up @@ -136,10 +136,10 @@ export async function load({ parent, fetch }) {
import { createQuery } from '@tanstack/svelte-query'
// This data is cached by prefetchQuery in +page.ts so no fetch actually happens here
const query = createQuery({
const query = createQuery(() => ({
queryKey: ['posts'],
queryFn: async () => (await fetch('/api/posts')).json(),
})
}))
</script>
```

Expand Down
1 change: 1 addition & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export default [
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-unsafe-function-type': 'off',
'no-case-declarations': 'off',
'prefer-const': 'off',
},
},
{
Expand Down
4 changes: 3 additions & 1 deletion examples/svelte/auto-refetching/src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import { QueryClientProvider, QueryClient } from '@tanstack/svelte-query'
import { SvelteQueryDevtools } from '@tanstack/svelte-query-devtools'
const { children } = $props()
const queryClient = new QueryClient({
defaultOptions: {
queries: {
Expand All @@ -15,7 +17,7 @@

<QueryClientProvider client={queryClient}>
<main>
<slot />
{@render children()}
</main>
<SvelteQueryDevtools />
</QueryClientProvider>
56 changes: 31 additions & 25 deletions examples/svelte/auto-refetching/src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,32 @@
createMutation,
} from '@tanstack/svelte-query'
let intervalMs = 1000
let value = ''
let intervalMs = $state(1000)
let value = $state<string>('')
const client = useQueryClient()
const endpoint = '/api/data'
$: todos = createQuery<{ items: string[] }>({
const todos = createQuery<{ items: string[] }>(() => ({
queryKey: ['refetch'],
queryFn: async () => await fetch(endpoint).then((r) => r.json()),
// Refetch the data every second
refetchInterval: intervalMs,
})
}))
const addMutation = createMutation({
const addMutation = createMutation(() => ({
mutationFn: (value: string) =>
fetch(`${endpoint}?add=${encodeURIComponent(value)}`).then((r) =>
r.json(),
),
onSuccess: () => client.invalidateQueries({ queryKey: ['refetch'] }),
})
}))
const clearMutation = createMutation({
const clearMutation = createMutation(() => ({
mutationFn: () => fetch(`${endpoint}?clear=1`).then((r) => r.json()),
onSuccess: () => client.invalidateQueries({ queryKey: ['refetch'] }),
})
}))
</script>

<h1>Auto Refetch with stale-time set to {intervalMs}ms</h1>
Expand All @@ -51,53 +51,59 @@
margin-left:.5rem;
width:.75rem;
height:.75rem;
background: {$todos.isFetching ? 'green' : 'transparent'};
transition: {!$todos.isFetching ? 'all .3s ease' : 'none'};
background: {todos.isFetching ? 'green' : 'transparent'};
transition: {!todos.isFetching ? 'all .3s ease' : 'none'};
border-radius: 100%;
transform: scale(1.5)"
></span>
</div>
</label>
<h2>Todo List</h2>
<form
on:submit={(e) => {
onsubmit={(e) => {
e.preventDefault()
e.stopPropagation()
$addMutation.mutate(value, {
addMutation.mutate(value, {
onSuccess: () => (value = ''),
})
}}
>
<input placeholder="enter something" bind:value />
</form>

{#if $todos.isPending}
{#if todos.isPending}
Loading...
{/if}
{#if $todos.error}
{#if todos.error}
An error has occurred:
{$todos.error.message}
{todos.error.message}
{/if}
{#if $todos.isSuccess}
{#if todos.isSuccess}
<ul>
{#each $todos.data.items as item}
{#each todos.data.items as item}
<li>{item}</li>
{/each}
</ul>
<div>
<button on:click={() => $clearMutation.mutate(undefined)}>
Clear All
</button>
</div>
{/if}
{#if $todos.isFetching}
<div style="color:darkgreen; font-weight:700">
'Background Updating...' : ' '
<button onclick={() => clearMutation.mutate(undefined)}> Clear All </button>
</div>
{/if}

<pre
class={['updating-text', todos.isFetching && 'on']}
style="font-weight:700">Background Updating...</pre>

<style>
li {
text-align: left;
}
.updating-text {
color: transparent;
transition: all 0.3s ease;
}
.updating-text.on {
color: green;
transition: none;
}
</style>
3 changes: 3 additions & 0 deletions examples/svelte/auto-refetching/svelte.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ const config = {
kit: {
adapter: adapter(),
},
compilerOptions: {
runes: true,
},
}

export default config
Loading
Loading