diff --git a/examples/react/transition/.eslintrc b/examples/react/transition/.eslintrc
new file mode 100644
index 0000000000..4e03b9e10b
--- /dev/null
+++ b/examples/react/transition/.eslintrc
@@ -0,0 +1,3 @@
+{
+ "extends": ["plugin:react/jsx-runtime", "plugin:react-hooks/recommended"]
+}
diff --git a/examples/react/transition/.gitignore b/examples/react/transition/.gitignore
new file mode 100644
index 0000000000..4673b022e5
--- /dev/null
+++ b/examples/react/transition/.gitignore
@@ -0,0 +1,27 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# production
+/build
+
+pnpm-lock.yaml
+yarn.lock
+package-lock.json
+
+# misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
diff --git a/examples/react/transition/README.md b/examples/react/transition/README.md
new file mode 100644
index 0000000000..93f18812e1
--- /dev/null
+++ b/examples/react/transition/README.md
@@ -0,0 +1,6 @@
+# Example
+
+To run this example:
+
+- `pnpm install`
+- `pnpm dev`
diff --git a/examples/react/transition/index.html b/examples/react/transition/index.html
new file mode 100644
index 0000000000..aca35c1a17
--- /dev/null
+++ b/examples/react/transition/index.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+ TanStack Query React Suspense Example App
+
+
+
+
+
+
+
diff --git a/examples/react/transition/package.json b/examples/react/transition/package.json
new file mode 100644
index 0000000000..17d5de31b4
--- /dev/null
+++ b/examples/react/transition/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "@tanstack/query-example-react-transition",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@tanstack/react-query": "^5.62.8",
+ "@tanstack/react-query-devtools": "^5.62.8",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0"
+ },
+ "devDependencies": {
+ "@vitejs/plugin-react": "^4.3.3",
+ "typescript": "5.7.2",
+ "vite": "^5.3.5"
+ }
+}
diff --git a/examples/react/transition/public/emblem-light.svg b/examples/react/transition/public/emblem-light.svg
new file mode 100644
index 0000000000..a58e69ad5e
--- /dev/null
+++ b/examples/react/transition/public/emblem-light.svg
@@ -0,0 +1,13 @@
+
+
\ No newline at end of file
diff --git a/examples/react/transition/src/index.tsx b/examples/react/transition/src/index.tsx
new file mode 100755
index 0000000000..a8f03fee3b
--- /dev/null
+++ b/examples/react/transition/src/index.tsx
@@ -0,0 +1,102 @@
+import {
+ QueryClient,
+ QueryClientProvider,
+ useQuery,
+} from '@tanstack/react-query'
+import { Suspense, use, useState, useTransition } from 'react'
+import ReactDOM from 'react-dom/client'
+
+const Example1 = ({ value }: { value: number }) => {
+ const { isFetching, promise } = useQuery({
+ queryKey: ['1' + value],
+ queryFn: async () => {
+ await new Promise((resolve) => setTimeout(resolve, 1000))
+ return '1' + value
+ },
+ })
+ const data = use(promise)
+
+ return (
+
+ {data} {isFetching ? 'fetching' : null}
+
+ )
+}
+
+const Example2 = ({ value }: { value: number }) => {
+ const { promise, isFetching } = useQuery({
+ queryKey: ['2' + value],
+ queryFn: async () => {
+ await new Promise((resolve) => setTimeout(resolve, 1000))
+ return '2' + value
+ },
+ // placeholderData: keepPreviousData,
+ })
+
+ const data = use(promise)
+
+ return (
+
+ {data} {isFetching ? 'fetching' : null}
+
+ )
+}
+
+const SuspenseBoundary = () => {
+ const [state, setState] = useState(-1)
+ const [isPending, startTransition] = useTransition()
+
+ return (
+
+
Change state with transition
+
+
+
+
State:
+
+ - last state value: {state}
+ -
+ transition state: {isPending ? pending : 'idle'}
+
+
+
2. 1 Suspense + startTransition
+
+
+
+
2.2 Suspense + startTransition
+
+
+
+
+ )
+}
+
+const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ experimental_prefetchInRender: true,
+ staleTime: 10 * 1000,
+ },
+ },
+})
+
+const App = () => {
+ return (
+
+
+
+
+
+ )
+}
+
+const rootElement = document.getElementById('root') as HTMLElement
+ReactDOM.createRoot(rootElement).render()
diff --git a/examples/react/transition/tsconfig.json b/examples/react/transition/tsconfig.json
new file mode 100644
index 0000000000..23a8707ef4
--- /dev/null
+++ b/examples/react/transition/tsconfig.json
@@ -0,0 +1,24 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "Bundler",
+ "allowImportingTsExtensions": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx",
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["src", "eslint.config.js"]
+}
diff --git a/examples/react/transition/vite.config.ts b/examples/react/transition/vite.config.ts
new file mode 100644
index 0000000000..9ffcc67574
--- /dev/null
+++ b/examples/react/transition/vite.config.ts
@@ -0,0 +1,6 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+
+export default defineConfig({
+ plugins: [react()],
+})
diff --git a/packages/react-query/src/__tests__/regression-8384-transition.test.tsx b/packages/react-query/src/__tests__/regression-8384-transition.test.tsx
new file mode 100644
index 0000000000..14a47804ed
--- /dev/null
+++ b/packages/react-query/src/__tests__/regression-8384-transition.test.tsx
@@ -0,0 +1,107 @@
+/* eslint-disable @typescript-eslint/require-await */
+import { act, render, screen } from '@testing-library/react'
+import * as React from 'react'
+import { afterAll, beforeAll, describe, expect, it } from 'vitest'
+import { QueryClientProvider, useQuery } from '..'
+import { QueryCache } from '../index'
+import { createQueryClient, queryKey, sleep } from './utils'
+
+describe('react transitions', () => {
+ const queryCache = new QueryCache()
+ const queryClient = createQueryClient({
+ queryCache,
+ })
+
+ beforeAll(() => {
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+ global.IS_REACT_ACT_ENVIRONMENT = true
+ queryClient.setDefaultOptions({
+ queries: { experimental_prefetchInRender: true },
+ })
+ })
+ afterAll(() => {
+ queryClient.setDefaultOptions({
+ queries: { experimental_prefetchInRender: false },
+ })
+ })
+
+ it('should keep values of old key around with startTransition', async () => {
+ const key = queryKey()
+ const resolveByCount: Record void> = {}
+
+ function Loading() {
+ return <>loading...>
+ }
+
+ function Page() {
+ const [isPending, startTransition] = React.useTransition()
+ const [count, setCount] = React.useState(0)
+ const query = useQuery({
+ queryKey: [key, count],
+ queryFn: async () => {
+ await new Promise((resolve) => {
+ resolveByCount[count] = resolve
+ })
+ return 'test' + count
+ },
+ })
+
+ const data = React.use(query.promise)
+
+ return (
+
+
+ {isPending &&
pending...}
+
data: {data}
+
+ )
+ }
+
+ // Initial render should show fallback
+ await act(async () => {
+ render(
+
+ }>
+
+
+ ,
+ )
+ })
+ screen.getByText('loading...')
+ expect(screen.queryByText('button')).toBeNull()
+ expect(screen.queryByText('pending...')).toBeNull()
+ expect(screen.queryByText('data: test0')).toBeNull()
+
+ // Resolve the query, should show the data
+ await act(async () => {
+ resolveByCount[0]!()
+ })
+ // HELP WANTED - get the below to fail as the repro does
+ expect(screen.queryByText('loading...')).toBeNull()
+ screen.getByRole('button')
+ expect(screen.queryByText('pending...')).toBeNull()
+ screen.getByText('data: test0')
+
+ // Update in a transition, should show pending state, and existing content
+ await act(async () => {
+ for (let i = 0; i < 100; i++) {
+ screen.getByRole('button', { name: 'increment' }).click()
+ }
+ })
+
+ // resolve all
+ for (const resolve of Object.values(resolveByCount)) {
+ await sleep(1)
+ await act(async () => {
+ resolve()
+ })
+ }
+
+ expect(screen.queryByText('loading...')).toBeNull()
+ expect(screen.queryByText('pending...')).toBeNull()
+ screen.getByText('data: test100')
+ })
+})
diff --git a/packages/react-query/vite.config.ts b/packages/react-query/vite.config.ts
index fba5f8d044..515825d864 100644
--- a/packages/react-query/vite.config.ts
+++ b/packages/react-query/vite.config.ts
@@ -10,7 +10,7 @@ export default defineConfig({
watch: false,
environment: 'jsdom',
setupFiles: ['test-setup.ts'],
- coverage: { enabled: true, provider: 'istanbul', include: ['src/**/*'] },
+ coverage: { enabled: false, provider: 'istanbul', include: ['src/**/*'] },
typecheck: { enabled: true },
restoreMocks: true,
retry: process.env.CI ? 3 : 0,
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ef044253a2..3d2f90c826 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1313,6 +1313,31 @@ importers:
specifier: ^5.3.5
version: 5.4.11(@types/node@22.9.3)(less@4.2.1)(lightningcss@1.27.0)(sass@1.81.0)(terser@5.31.6)
+ examples/react/transition:
+ dependencies:
+ '@tanstack/react-query':
+ specifier: ^5.62.8
+ version: link:../../../packages/react-query
+ '@tanstack/react-query-devtools':
+ specifier: ^5.62.8
+ version: link:../../../packages/react-query-devtools
+ react:
+ specifier: ^19.0.0
+ version: 19.0.0
+ react-dom:
+ specifier: ^19.0.0
+ version: 19.0.0(react@19.0.0)
+ devDependencies:
+ '@vitejs/plugin-react':
+ specifier: ^4.3.3
+ version: 4.3.3(vite@5.4.11(@types/node@22.9.3)(less@4.2.1)(lightningcss@1.27.0)(sass@1.81.0)(terser@5.31.6))
+ typescript:
+ specifier: 5.7.2
+ version: 5.7.2
+ vite:
+ specifier: ^5.3.5
+ version: 5.4.11(@types/node@22.9.3)(less@4.2.1)(lightningcss@1.27.0)(sass@1.81.0)(terser@5.31.6)
+
examples/solid/astro:
dependencies:
'@astrojs/check':