diff --git a/docs/config.json b/docs/config.json
index 29b9c545ad..d905826fb4 100644
--- a/docs/config.json
+++ b/docs/config.json
@@ -167,6 +167,31 @@
"to": "framework/angular/zoneless"
}
]
+ },
+ {
+ "label": "preact",
+ "children": [
+ {
+ "label": "Overview",
+ "to": "framework/preact/overview"
+ },
+ {
+ "label": "Installation",
+ "to": "framework/preact/installation"
+ },
+ {
+ "label": "Quick Start",
+ "to": "framework/preact/quick-start"
+ },
+ {
+ "label": "TypeScript",
+ "to": "framework/preact/typescript"
+ },
+ {
+ "label": "GraphQL",
+ "to": "framework/preact/graphql"
+ }
+ ]
}
]
},
@@ -717,6 +742,131 @@
"to": "framework/angular/guides/does-this-replace-client-state"
}
]
+ },
+ {
+ "label": "preact",
+ "children": [
+ {
+ "label": "Important Defaults",
+ "to": "framework/preact/guides/important-defaults"
+ },
+ {
+ "label": "Queries",
+ "to": "framework/preact/guides/queries"
+ },
+ {
+ "label": "Query Keys",
+ "to": "framework/preact/guides/query-keys"
+ },
+ {
+ "label": "Query Functions",
+ "to": "framework/preact/guides/query-functions"
+ },
+ {
+ "label": "Query Options",
+ "to": "framework/preact/guides/query-options"
+ },
+ {
+ "label": "Network Mode",
+ "to": "framework/preact/guides/network-mode"
+ },
+ {
+ "label": "Parallel Queries",
+ "to": "framework/preact/guides/parallel-queries"
+ },
+ {
+ "label": "Dependent Queries",
+ "to": "framework/preact/guides/dependent-queries"
+ },
+ {
+ "label": "Background Fetching Indicators",
+ "to": "framework/preact/guides/background-fetching-indicators"
+ },
+ {
+ "label": "Window Focus Refetching",
+ "to": "framework/preact/guides/window-focus-refetching"
+ },
+ {
+ "label": "Disabling/Pausing Queries",
+ "to": "framework/preact/guides/disabling-queries"
+ },
+ {
+ "label": "Query Retries",
+ "to": "framework/preact/guides/query-retries"
+ },
+ {
+ "label": "Paginated Queries",
+ "to": "framework/preact/guides/paginated-queries"
+ },
+ {
+ "label": "Infinite Queries",
+ "to": "framework/preact/guides/infinite-queries"
+ },
+ {
+ "label": "Initial Query Data",
+ "to": "framework/preact/guides/initial-query-data"
+ },
+ {
+ "label": "Placeholder Query Data",
+ "to": "framework/preact/guides/placeholder-query-data"
+ },
+ {
+ "label": "Mutations",
+ "to": "framework/preact/guides/mutations"
+ },
+ {
+ "label": "Query Invalidation",
+ "to": "framework/preact/guides/query-invalidation"
+ },
+ {
+ "label": "Invalidation from Mutations",
+ "to": "framework/preact/guides/invalidations-from-mutations"
+ },
+ {
+ "label": "Updates from Mutation Responses",
+ "to": "framework/preact/guides/updates-from-mutation-responses"
+ },
+ {
+ "label": "Optimistic Updates",
+ "to": "framework/preact/guides/optimistic-updates"
+ },
+ {
+ "label": "Query Cancellation",
+ "to": "framework/preact/guides/query-cancellation"
+ },
+ {
+ "label": "Scroll Restoration",
+ "to": "framework/preact/guides/scroll-restoration"
+ },
+ {
+ "label": "Filters",
+ "to": "framework/preact/guides/filters"
+ },
+ {
+ "label": "Performance & Request Waterfalls",
+ "to": "framework/preact/guides/request-waterfalls"
+ },
+ {
+ "label": "Prefetching & Router Integration",
+ "to": "framework/preact/guides/prefetching"
+ },
+ {
+ "label": "Caching",
+ "to": "framework/preact/guides/caching"
+ },
+ {
+ "label": "Render Optimizations",
+ "to": "framework/preact/guides/render-optimizations"
+ },
+ {
+ "label": "Default Query Fn",
+ "to": "framework/preact/guides/default-query-function"
+ },
+ {
+ "label": "Does this replace [Redux, MobX, etc]?",
+ "to": "framework/preact/guides/does-this-replace-client-state"
+ }
+ ]
}
]
},
@@ -1009,6 +1159,91 @@
"to": "framework/angular/reference/functions/injectMutation"
}
]
+ },
+ {
+ "label": "preact",
+ "children": [
+ {
+ "label": "useQuery",
+ "to": "framework/preact/reference/functions/useQuery"
+ },
+ {
+ "label": "useQueries",
+ "to": "framework/preact/reference/functions/useQueries"
+ },
+ {
+ "label": "useInfiniteQuery",
+ "to": "framework/preact/reference/functions/useInfiniteQuery"
+ },
+ {
+ "label": "useMutation",
+ "to": "framework/preact/reference/functions/useMutation"
+ },
+ {
+ "label": "useIsFetching",
+ "to": "framework/preact/reference/functions/useIsFetching"
+ },
+ {
+ "label": "useIsMutating",
+ "to": "framework/preact/reference/functions/useIsMutating"
+ },
+ {
+ "label": "useMutationState",
+ "to": "framework/preact/reference/functions/useMutationState"
+ },
+ {
+ "label": "useSuspenseQuery",
+ "to": "framework/preact/reference/functions/useSuspenseQuery"
+ },
+ {
+ "label": "useSuspenseInfiniteQuery",
+ "to": "framework/preact/reference/functions/useSuspenseInfiniteQuery"
+ },
+ {
+ "label": "useSuspenseQueries",
+ "to": "framework/preact/reference/functions/useSuspenseQueries"
+ },
+ {
+ "label": "QueryClientProvider",
+ "to": "framework/preact/reference/functions/QueryClientProvider"
+ },
+ {
+ "label": "useQueryClient",
+ "to": "framework/preact/reference/functions/useQueryClient"
+ },
+ {
+ "label": "queryOptions",
+ "to": "framework/preact/reference/functions/queryOptions"
+ },
+ {
+ "label": "infiniteQueryOptions",
+ "to": "framework/preact/reference/functions/infiniteQueryOptions"
+ },
+ {
+ "label": "mutationOptions",
+ "to": "framework/preact/reference/functions/mutationOptions"
+ },
+ {
+ "label": "usePrefetchQuery",
+ "to": "framework/preact/reference/functions/usePrefetchQuery"
+ },
+ {
+ "label": "usePrefetchInfiniteQuery",
+ "to": "framework/preact/reference/functions/usePrefetchInfiniteQuery"
+ },
+ {
+ "label": "QueryErrorResetBoundary",
+ "to": "framework/preact/reference/functions/QueryErrorResetBoundary"
+ },
+ {
+ "label": "useQueryErrorResetBoundary",
+ "to": "framework/preact/reference/functions/useQueryErrorResetBoundary"
+ },
+ {
+ "label": "hydration",
+ "to": "framework/react/reference/functions/HydrationBoundary"
+ }
+ ]
}
]
},
diff --git a/docs/framework/angular/guides/infinite-queries.md b/docs/framework/angular/guides/infinite-queries.md
index 9fdde83e26..69f5ac4fec 100644
--- a/docs/framework/angular/guides/infinite-queries.md
+++ b/docs/framework/angular/guides/infinite-queries.md
@@ -51,18 +51,20 @@ export class Example {
```angular-html
@if (query.isPending()) {
-
Loading...
+
Loading...
} @else if (query.isError()) {
-
Error: {{ query?.error().message }}
- } @else { @for (page of query?.data().pages; track $index) { @for (project of
- page.data; track project.id) {
-
{{ project.name }} {{ project.id }}
- } }
-
-
- {{ nextButtonText() }}
-
-
+
Error: {{ query?.error().message }}
+ } @else {
+ @for (page of query?.data().pages; track $index) {
+ @for (project of page.data; track project.id) {
+
{{ project.name }} {{ project.id }}
+ }
+ }
+
+
+ {{ nextButtonText() }}
+
+
}
```
diff --git a/docs/framework/angular/overview.md b/docs/framework/angular/overview.md
index be68c08e5a..69ebf2eac3 100644
--- a/docs/framework/angular/overview.md
+++ b/docs/framework/angular/overview.md
@@ -84,7 +84,7 @@ import { lastValueFrom } from 'rxjs'
✨ {{ data.stargazers_count }}
🍴 {{ data.forks_count }}
}
- `
+ `,
})
export class SimpleExampleComponent {
http = inject(HttpClient)
diff --git a/docs/framework/angular/quick-start.md b/docs/framework/angular/quick-start.md
index b711f92b2b..462ca0b17a 100644
--- a/docs/framework/angular/quick-start.md
+++ b/docs/framework/angular/quick-start.md
@@ -51,7 +51,7 @@ import { lastValueFrom } from 'rxjs'
import {
injectMutation,
injectQuery,
- QueryClient
+ QueryClient,
} from '@tanstack/angular-query-experimental'
@Component({
diff --git a/docs/framework/angular/typescript.md b/docs/framework/angular/typescript.md
index 0aa2ff2638..de3fd49758 100644
--- a/docs/framework/angular/typescript.md
+++ b/docs/framework/angular/typescript.md
@@ -78,7 +78,7 @@ class MyComponent {
template: `
@if (query.isSuccess()) {
@let data = query.data();
- // ^? data: number
+ // ^? data: number
}
`,
})
@@ -104,7 +104,7 @@ class MyComponent {
class MyComponent {
query = injectQuery(() => ({
queryKey: ['groups'],
- queryFn: fetchGroups
+ queryFn: fetchGroups,
}))
}
```
diff --git a/docs/framework/preact/graphql.md b/docs/framework/preact/graphql.md
new file mode 100644
index 0000000000..d224bbe68a
--- /dev/null
+++ b/docs/framework/preact/graphql.md
@@ -0,0 +1,6 @@
+---
+id: graphql
+title: GraphQL
+ref: docs/framework/react/graphql.md
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/guides/background-fetching-indicators.md b/docs/framework/preact/guides/background-fetching-indicators.md
new file mode 100644
index 0000000000..1227175d25
--- /dev/null
+++ b/docs/framework/preact/guides/background-fetching-indicators.md
@@ -0,0 +1,6 @@
+---
+id: background-fetching-indicators
+title: Background Fetching Indicators
+ref: docs/framework/react/guides/background-fetching-indicators.md
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/guides/caching.md b/docs/framework/preact/guides/caching.md
new file mode 100644
index 0000000000..3ef32c5885
--- /dev/null
+++ b/docs/framework/preact/guides/caching.md
@@ -0,0 +1,6 @@
+---
+id: caching
+title: Caching Examples
+ref: docs/framework/react/guides/caching.md
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/guides/default-query-function.md b/docs/framework/preact/guides/default-query-function.md
new file mode 100644
index 0000000000..07b0bfa107
--- /dev/null
+++ b/docs/framework/preact/guides/default-query-function.md
@@ -0,0 +1,6 @@
+---
+id: default-query-function
+title: Default Query Function
+ref: docs/framework/react/guides/default-query-function.md
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/guides/dependent-queries.md b/docs/framework/preact/guides/dependent-queries.md
new file mode 100644
index 0000000000..d297a0fcfd
--- /dev/null
+++ b/docs/framework/preact/guides/dependent-queries.md
@@ -0,0 +1,6 @@
+---
+id: dependent-queries
+title: Dependent Queries
+ref: docs/framework/react/guides/dependent-queries.md
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/guides/disabling-queries.md b/docs/framework/preact/guides/disabling-queries.md
new file mode 100644
index 0000000000..a321227bce
--- /dev/null
+++ b/docs/framework/preact/guides/disabling-queries.md
@@ -0,0 +1,56 @@
+---
+id: disabling-queries
+title: Disabling/Pausing Queries
+ref: docs/framework/react/guides/disabling-queries.md
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
+
+[//]: # 'Example2'
+
+```tsx
+function Todos() {
+ const [filter, setFilter] = useState('')
+
+ const { data } = useQuery({
+ queryKey: ['todos', filter],
+ queryFn: () => fetchTodos(filter),
+ // ⬇️ disabled as long as the filter is empty
+ enabled: !!filter,
+ })
+
+ return (
+
+ // 🚀 applying the filter will enable and execute the query
+
+ {data && }
+
+ )
+}
+```
+
+[//]: # 'Example2'
+[//]: # 'Example3'
+
+```tsx
+import { skipToken, useQuery } from '@tanstack/preact-query'
+
+function Todos() {
+ const [filter, setFilter] = useState()
+
+ const { data } = useQuery({
+ queryKey: ['todos', filter],
+ // ⬇️ disabled as long as the filter is undefined or empty
+ queryFn: filter ? () => fetchTodos(filter) : skipToken,
+ })
+
+ return (
+
+ // 🚀 applying the filter will enable and execute the query
+
+ {data && }
+
+ )
+}
+```
+
+[//]: # 'Example3'
diff --git a/docs/framework/preact/guides/does-this-replace-client-state.md b/docs/framework/preact/guides/does-this-replace-client-state.md
new file mode 100644
index 0000000000..63efcd390e
--- /dev/null
+++ b/docs/framework/preact/guides/does-this-replace-client-state.md
@@ -0,0 +1,6 @@
+---
+id: does-this-replace-client-state
+title: Does TanStack Query replace Redux, MobX or other global state managers?
+ref: docs/framework/react/guides/does-this-replace-client-state
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/guides/filters.md b/docs/framework/preact/guides/filters.md
new file mode 100644
index 0000000000..3ad4af2732
--- /dev/null
+++ b/docs/framework/preact/guides/filters.md
@@ -0,0 +1,6 @@
+---
+id: filters
+title: Filters
+ref: docs/framework/react/guides/filters
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/guides/important-defaults.md b/docs/framework/preact/guides/important-defaults.md
new file mode 100644
index 0000000000..45ba8ecbe5
--- /dev/null
+++ b/docs/framework/preact/guides/important-defaults.md
@@ -0,0 +1,6 @@
+---
+id: important-defaults
+title: Important Defaults
+ref: docs/framework/react/guides/important-defaults
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/guides/infinite-queries.md b/docs/framework/preact/guides/infinite-queries.md
new file mode 100644
index 0000000000..358e31499c
--- /dev/null
+++ b/docs/framework/preact/guides/infinite-queries.md
@@ -0,0 +1,65 @@
+---
+id: infinite-queries
+title: Infinite Queries
+ref: docs/framework/react/guides/infinite-queries
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
+
+[//]: # 'Example'
+
+```tsx
+import { useInfiniteQuery } from '@tanstack/preact-query'
+
+function Projects() {
+ const fetchProjects = async ({ pageParam }) => {
+ const res = await fetch('/api/projects?cursor=' + pageParam)
+ return res.json()
+ }
+
+ const {
+ data,
+ error,
+ fetchNextPage,
+ hasNextPage,
+ isFetching,
+ isFetchingNextPage,
+ status,
+ } = useInfiniteQuery({
+ queryKey: ['projects'],
+ queryFn: fetchProjects,
+ initialPageParam: 0,
+ getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
+ })
+
+ return status === 'pending' ? (
+ Loading...
+ ) : status === 'error' ? (
+ Error: {error.message}
+ ) : (
+ <>
+ {data.pages.map((group, i) => (
+
+ {group.data.map((project) => (
+
{project.name}
+ ))}
+
+ ))}
+
+ fetchNextPage()}
+ disabled={!hasNextPage || isFetching}
+ >
+ {isFetchingNextPage
+ ? 'Loading more...'
+ : hasNextPage
+ ? 'Load More'
+ : 'Nothing more to load'}
+
+
+ {isFetching && !isFetchingNextPage ? 'Fetching...' : null}
+ >
+ )
+}
+```
+
+[//]: # 'Example'
diff --git a/docs/framework/preact/guides/initial-query-data.md b/docs/framework/preact/guides/initial-query-data.md
new file mode 100644
index 0000000000..d0cb43ac6c
--- /dev/null
+++ b/docs/framework/preact/guides/initial-query-data.md
@@ -0,0 +1,6 @@
+---
+id: initial-query-data
+title: Initial Query Data
+ref: docs/framework/react/guides/initial-query-data
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/guides/invalidations-from-mutations.md b/docs/framework/preact/guides/invalidations-from-mutations.md
new file mode 100644
index 0000000000..fb3495723b
--- /dev/null
+++ b/docs/framework/preact/guides/invalidations-from-mutations.md
@@ -0,0 +1,6 @@
+---
+id: invalidations-from-mutations
+title: Invalidations from Mutations
+ref: docs/framework/react/guides/invalidations-from-mutations
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/guides/mutations.md b/docs/framework/preact/guides/mutations.md
new file mode 100644
index 0000000000..4e4915e8a3
--- /dev/null
+++ b/docs/framework/preact/guides/mutations.md
@@ -0,0 +1,11 @@
+---
+id: mutations
+title: Mutations
+ref: docs/framework/react/guides/mutations
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
+
+[//]: # 'Info1'
+[//]: # 'Info1'
+[//]: # 'Example2'
+[//]: # 'Example2'
diff --git a/docs/framework/preact/guides/network-mode.md b/docs/framework/preact/guides/network-mode.md
new file mode 100644
index 0000000000..e3e8c40d82
--- /dev/null
+++ b/docs/framework/preact/guides/network-mode.md
@@ -0,0 +1,6 @@
+---
+id: network-mode
+title: Network Mode
+ref: docs/framework/react/guides/network-mode
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/guides/optimistic-updates.md b/docs/framework/preact/guides/optimistic-updates.md
new file mode 100644
index 0000000000..93816d36d0
--- /dev/null
+++ b/docs/framework/preact/guides/optimistic-updates.md
@@ -0,0 +1,6 @@
+---
+id: optimistic-updates
+title: Optimistic Updates
+ref: docs/framework/react/guides/optimistic-updates
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/guides/paginated-queries.md b/docs/framework/preact/guides/paginated-queries.md
new file mode 100644
index 0000000000..6d408be5f9
--- /dev/null
+++ b/docs/framework/preact/guides/paginated-queries.md
@@ -0,0 +1,64 @@
+---
+id: paginated-queries
+title: Paginated / Lagged Queries
+ref: docs/framework/react/guides/paginated-queries
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
+
+[//]: # 'Example2'
+
+```tsx
+import { keepPreviousData, useQuery } from '@tanstack/preact-query'
+import { useState } from 'preact/hooks'
+
+function Todos() {
+ const [page, setPage] = useState(0)
+
+ const fetchProjects = (page = 0) =>
+ fetch('/api/projects?page=' + page).then((res) => res.json())
+
+ const { isPending, isError, error, data, isFetching, isPlaceholderData } =
+ useQuery({
+ queryKey: ['projects', page],
+ queryFn: () => fetchProjects(page),
+ placeholderData: keepPreviousData,
+ })
+
+ return (
+
+ {isPending ? (
+
Loading...
+ ) : isError ? (
+
Error: {error.message}
+ ) : (
+
+ {data.projects.map((project) => (
+
{project.name}
+ ))}
+
+ )}
+
Current Page: {page + 1}
+
setPage((old) => Math.max(old - 1, 0))}
+ disabled={page === 0}
+ >
+ Previous Page
+
+
{
+ if (!isPlaceholderData && data.hasMore) {
+ setPage((old) => old + 1)
+ }
+ }}
+ // Disable the Next Page button until we know a next page is available
+ disabled={isPlaceholderData || !data?.hasMore}
+ >
+ Next Page
+
+ {isFetching ?
Loading... : null}
+
+ )
+}
+```
+
+[//]: # 'Example2'
diff --git a/docs/framework/preact/guides/parallel-queries.md b/docs/framework/preact/guides/parallel-queries.md
new file mode 100644
index 0000000000..b734affa5c
--- /dev/null
+++ b/docs/framework/preact/guides/parallel-queries.md
@@ -0,0 +1,12 @@
+---
+id: parallel-queries
+title: Parallel Queries
+ref: docs/framework/react/guides/parallel-queries
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
+
+[//]: # 'Info'
+
+> When using Preact Query with compat's Suspense, this pattern of parallelism does not work, since the first query would throw a promise internally and would suspend the component before the other queries run. To get around this, you'll either need to use the `useSuspenseQueries` hook (which is suggested) or orchestrate your own parallelism with separate components for each `useSuspenseQuery` instance.
+
+[//]: # 'Info'
diff --git a/docs/framework/preact/guides/placeholder-query-data.md b/docs/framework/preact/guides/placeholder-query-data.md
new file mode 100644
index 0000000000..abab8d6516
--- /dev/null
+++ b/docs/framework/preact/guides/placeholder-query-data.md
@@ -0,0 +1,6 @@
+---
+id: placeholder-query-data
+title: Placeholder Query Data
+ref: docs/framework/react/guides/placeholder-query-data
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/guides/prefetching.md b/docs/framework/preact/guides/prefetching.md
new file mode 100644
index 0000000000..027f5e385e
--- /dev/null
+++ b/docs/framework/preact/guides/prefetching.md
@@ -0,0 +1,51 @@
+---
+id: prefetching
+title: Prefetching & Router Integration
+ref: docs/framework/react/guides/prefetching
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
+
+[//]: # 'ExampleConditionally1'
+
+```tsx
+// This lazy loads the GraphFeedItem component, meaning
+// it wont start loading until something renders it
+import { lazy } from 'preact/iso'
+
+const GraphFeedItem = lazy(() => import('./GraphFeedItem'))
+
+function Feed() {
+ const { data, isPending } = useQuery({
+ queryKey: ['feed'],
+ queryFn: getFeed,
+ })
+
+ if (isPending) {
+ return 'Loading feed...'
+ }
+
+ return (
+ <>
+ {data.map((feedItem) => {
+ if (feedItem.type === 'GRAPH') {
+ return
+ }
+
+ return
+ })}
+ >
+ )
+}
+
+// GraphFeedItem.tsx
+function GraphFeedItem({ feedItem }) {
+ const { data, isPending } = useQuery({
+ queryKey: ['graph', feedItem.id],
+ queryFn: getGraphDataById,
+ })
+
+ ...
+}
+```
+
+[//]: # 'ExampleConditionally1'
diff --git a/docs/framework/preact/guides/queries.md b/docs/framework/preact/guides/queries.md
new file mode 100644
index 0000000000..567bd63ba8
--- /dev/null
+++ b/docs/framework/preact/guides/queries.md
@@ -0,0 +1,6 @@
+---
+id: queries
+title: Queries
+ref: docs/framework/react/guides/queries
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/guides/query-cancellation.md b/docs/framework/preact/guides/query-cancellation.md
new file mode 100644
index 0000000000..32b789b7c5
--- /dev/null
+++ b/docs/framework/preact/guides/query-cancellation.md
@@ -0,0 +1,6 @@
+---
+id: query-cancellation
+title: Query Cancellation
+ref: docs/framework/react/guides/query-cancellation
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/guides/query-functions.md b/docs/framework/preact/guides/query-functions.md
new file mode 100644
index 0000000000..ac79972f00
--- /dev/null
+++ b/docs/framework/preact/guides/query-functions.md
@@ -0,0 +1,6 @@
+---
+id: query-functions
+title: Query Functions
+ref: docs/framework/react/guides/query-functions
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/guides/query-invalidation.md b/docs/framework/preact/guides/query-invalidation.md
new file mode 100644
index 0000000000..7aab743f34
--- /dev/null
+++ b/docs/framework/preact/guides/query-invalidation.md
@@ -0,0 +1,6 @@
+---
+id: query-invalidation
+title: Query Invalidation
+ref: docs/framework/react/guides/query-invalidation
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/guides/query-keys.md b/docs/framework/preact/guides/query-keys.md
new file mode 100644
index 0000000000..038cea0c65
--- /dev/null
+++ b/docs/framework/preact/guides/query-keys.md
@@ -0,0 +1,6 @@
+---
+id: query-keys
+title: Query Keys
+ref: docs/framework/react/guides/query-keys
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/guides/query-options.md b/docs/framework/preact/guides/query-options.md
new file mode 100644
index 0000000000..a5eb7d3370
--- /dev/null
+++ b/docs/framework/preact/guides/query-options.md
@@ -0,0 +1,6 @@
+---
+id: query-options
+title: Query Options
+ref: docs/framework/react/guides/query-options
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/guides/query-retries.md b/docs/framework/preact/guides/query-retries.md
new file mode 100644
index 0000000000..b61d199b48
--- /dev/null
+++ b/docs/framework/preact/guides/query-retries.md
@@ -0,0 +1,6 @@
+---
+id: query-retries
+title: Query Retries
+ref: docs/framework/react/guides/query-retries
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/guides/render-optimizations.md b/docs/framework/preact/guides/render-optimizations.md
new file mode 100644
index 0000000000..cd922bf637
--- /dev/null
+++ b/docs/framework/preact/guides/render-optimizations.md
@@ -0,0 +1,6 @@
+---
+id: render-optimizations
+title: Render Optimizations
+ref: docs/framework/react/guides/render-optimizations
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/guides/request-waterfalls.md b/docs/framework/preact/guides/request-waterfalls.md
new file mode 100644
index 0000000000..a7524f691f
--- /dev/null
+++ b/docs/framework/preact/guides/request-waterfalls.md
@@ -0,0 +1,50 @@
+---
+id: request-waterfalls
+title: Performance & Request Waterfalls
+ref: docs/framework/react/guides/request-waterfalls
+replace: { 'react': 'preact', 'react-query': 'preact-query' }
+---
+
+[//]: # 'LazyExample'
+
+```tsx
+import { lazy } from 'preact/iso'
+// This lazy loads the GraphFeedItem component, meaning
+// it wont start loading until something renders it
+const GraphFeedItem = lazy(() => import('./GraphFeedItem'))
+
+function Feed() {
+ const { data, isPending } = useQuery({
+ queryKey: ['feed'],
+ queryFn: getFeed,
+ })
+
+ if (isPending) {
+ return 'Loading feed...'
+ }
+
+ return (
+ <>
+ {data.map((feedItem) => {
+ if (feedItem.type === 'GRAPH') {
+ return
+ }
+
+ return
+ })}
+ >
+ )
+}
+
+// GraphFeedItem.tsx
+function GraphFeedItem({ feedItem }) {
+ const { data, isPending } = useQuery({
+ queryKey: ['graph', feedItem.id],
+ queryFn: getGraphDataById,
+ })
+
+ ...
+}
+```
+
+[//]: # 'LazyExample'
diff --git a/docs/framework/preact/guides/scroll-restoration.md b/docs/framework/preact/guides/scroll-restoration.md
new file mode 100644
index 0000000000..ae9b007b20
--- /dev/null
+++ b/docs/framework/preact/guides/scroll-restoration.md
@@ -0,0 +1,6 @@
+---
+id: scroll-restoration
+title: Scroll Restoration
+ref: docs/framework/react/guides/scroll-restoration.md
+replace: { 'react': 'preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/guides/updates-from-mutation-responses.md b/docs/framework/preact/guides/updates-from-mutation-responses.md
new file mode 100644
index 0000000000..f1682e1382
--- /dev/null
+++ b/docs/framework/preact/guides/updates-from-mutation-responses.md
@@ -0,0 +1,6 @@
+---
+id: updates-from-mutation-responses
+title: Updates from Mutation Responses
+ref: docs/framework/react/guides/updates-from-mutation-responses.md
+replace: { 'react': 'preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/guides/window-focus-refetching.md b/docs/framework/preact/guides/window-focus-refetching.md
new file mode 100644
index 0000000000..da6b3bb703
--- /dev/null
+++ b/docs/framework/preact/guides/window-focus-refetching.md
@@ -0,0 +1,9 @@
+---
+id: window-focus-refetching
+title: Window Focus Refetching
+ref: docs/framework/react/guides/window-focus-refetching.md
+replace: { 'react': 'preact', 'react-query': 'preact-query' }
+---
+
+[//]: # 'ReactNative'
+[//]: # 'ReactNative'
diff --git a/docs/framework/preact/installation.md b/docs/framework/preact/installation.md
new file mode 100644
index 0000000000..52fd069ca1
--- /dev/null
+++ b/docs/framework/preact/installation.md
@@ -0,0 +1,6 @@
+---
+id: installation
+title: Installation
+ref: docs/framework/react/installation.md
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/overview.md b/docs/framework/preact/overview.md
new file mode 100644
index 0000000000..cfb7ab00c4
--- /dev/null
+++ b/docs/framework/preact/overview.md
@@ -0,0 +1,6 @@
+---
+id: overview
+title: Overview
+ref: docs/framework/react/overview.md
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/plugins/broadcastQueryClient.md b/docs/framework/preact/plugins/broadcastQueryClient.md
new file mode 100644
index 0000000000..32cb1e50cf
--- /dev/null
+++ b/docs/framework/preact/plugins/broadcastQueryClient.md
@@ -0,0 +1,5 @@
+---
+id: broadcastQueryClient
+title: broadcastQueryClient (Experimental)
+ref: docs/framework/react/plugins/broadcastQueryClient.md
+---
diff --git a/docs/framework/preact/plugins/persistQueryClient.md b/docs/framework/preact/plugins/persistQueryClient.md
new file mode 100644
index 0000000000..cf261fc933
--- /dev/null
+++ b/docs/framework/preact/plugins/persistQueryClient.md
@@ -0,0 +1,6 @@
+---
+id: persistQueryClient
+title: persistQueryClient
+ref: docs/framework/react/plugins/persistQueryClient.md
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/quick-start.md b/docs/framework/preact/quick-start.md
new file mode 100644
index 0000000000..7dd0b870b3
--- /dev/null
+++ b/docs/framework/preact/quick-start.md
@@ -0,0 +1,6 @@
+---
+id: quick-start
+title: Quick Start
+ref: docs/framework/react/quick-start.md
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/preact/reference/functions/HydrationBoundary.md b/docs/framework/preact/reference/functions/HydrationBoundary.md
new file mode 100644
index 0000000000..6f2c2e9300
--- /dev/null
+++ b/docs/framework/preact/reference/functions/HydrationBoundary.md
@@ -0,0 +1,22 @@
+---
+id: HydrationBoundary
+title: HydrationBoundary
+---
+
+# Function: HydrationBoundary()
+
+```ts
+function HydrationBoundary(__namedParameters): ComponentChildren;
+```
+
+Defined in: [preact-query/src/HydrationBoundary.tsx:24](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/HydrationBoundary.tsx#L24)
+
+## Parameters
+
+### \_\_namedParameters
+
+[`HydrationBoundaryProps`](../interfaces/HydrationBoundaryProps.md)
+
+## Returns
+
+`ComponentChildren`
diff --git a/docs/framework/preact/reference/functions/QueryClientProvider.md b/docs/framework/preact/reference/functions/QueryClientProvider.md
new file mode 100644
index 0000000000..5c27789234
--- /dev/null
+++ b/docs/framework/preact/reference/functions/QueryClientProvider.md
@@ -0,0 +1,22 @@
+---
+id: QueryClientProvider
+title: QueryClientProvider
+---
+
+# Function: QueryClientProvider()
+
+```ts
+function QueryClientProvider(__namedParameters): VNode;
+```
+
+Defined in: [preact-query/src/QueryClientProvider.tsx:28](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/QueryClientProvider.tsx#L28)
+
+## Parameters
+
+### \_\_namedParameters
+
+[`QueryClientProviderProps`](../type-aliases/QueryClientProviderProps.md)
+
+## Returns
+
+`VNode`
diff --git a/docs/framework/preact/reference/functions/QueryErrorResetBoundary.md b/docs/framework/preact/reference/functions/QueryErrorResetBoundary.md
new file mode 100644
index 0000000000..73d8187efc
--- /dev/null
+++ b/docs/framework/preact/reference/functions/QueryErrorResetBoundary.md
@@ -0,0 +1,22 @@
+---
+id: QueryErrorResetBoundary
+title: QueryErrorResetBoundary
+---
+
+# Function: QueryErrorResetBoundary()
+
+```ts
+function QueryErrorResetBoundary(__namedParameters): Element;
+```
+
+Defined in: [preact-query/src/QueryErrorResetBoundary.tsx:47](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/QueryErrorResetBoundary.tsx#L47)
+
+## Parameters
+
+### \_\_namedParameters
+
+[`QueryErrorResetBoundaryProps`](../interfaces/QueryErrorResetBoundaryProps.md)
+
+## Returns
+
+`Element`
diff --git a/docs/framework/preact/reference/functions/infiniteQueryOptions.md b/docs/framework/preact/reference/functions/infiniteQueryOptions.md
new file mode 100644
index 0000000000..b3999370f9
--- /dev/null
+++ b/docs/framework/preact/reference/functions/infiniteQueryOptions.md
@@ -0,0 +1,126 @@
+---
+id: infiniteQueryOptions
+title: infiniteQueryOptions
+---
+
+# Function: infiniteQueryOptions()
+
+## Call Signature
+
+```ts
+function infiniteQueryOptions(options): UseInfiniteQueryOptions & object & object;
+```
+
+Defined in: [preact-query/src/infiniteQueryOptions.ts:75](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/infiniteQueryOptions.ts#L75)
+
+### Type Parameters
+
+#### TQueryFnData
+
+`TQueryFnData`
+
+#### TError
+
+`TError` = `Error`
+
+#### TData
+
+`TData` = `InfiniteData`\<`TQueryFnData`, `unknown`\>
+
+#### TQueryKey
+
+`TQueryKey` *extends* readonly `unknown`[] = readonly `unknown`[]
+
+#### TPageParam
+
+`TPageParam` = `unknown`
+
+### Parameters
+
+#### options
+
+[`DefinedInitialDataInfiniteOptions`](../type-aliases/DefinedInitialDataInfiniteOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\>
+
+### Returns
+
+[`UseInfiniteQueryOptions`](../interfaces/UseInfiniteQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> & `object` & `object`
+
+## Call Signature
+
+```ts
+function infiniteQueryOptions(options): OmitKeyof, "queryFn"> & object & object;
+```
+
+Defined in: [preact-query/src/infiniteQueryOptions.ts:99](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/infiniteQueryOptions.ts#L99)
+
+### Type Parameters
+
+#### TQueryFnData
+
+`TQueryFnData`
+
+#### TError
+
+`TError` = `Error`
+
+#### TData
+
+`TData` = `InfiniteData`\<`TQueryFnData`, `unknown`\>
+
+#### TQueryKey
+
+`TQueryKey` *extends* readonly `unknown`[] = readonly `unknown`[]
+
+#### TPageParam
+
+`TPageParam` = `unknown`
+
+### Parameters
+
+#### options
+
+[`UnusedSkipTokenInfiniteOptions`](../type-aliases/UnusedSkipTokenInfiniteOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\>
+
+### Returns
+
+`OmitKeyof`\<[`UseInfiniteQueryOptions`](../interfaces/UseInfiniteQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\>, `"queryFn"`\> & `object` & `object`
+
+## Call Signature
+
+```ts
+function infiniteQueryOptions(options): UseInfiniteQueryOptions & object & object;
+```
+
+Defined in: [preact-query/src/infiniteQueryOptions.ts:123](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/infiniteQueryOptions.ts#L123)
+
+### Type Parameters
+
+#### TQueryFnData
+
+`TQueryFnData`
+
+#### TError
+
+`TError` = `Error`
+
+#### TData
+
+`TData` = `InfiniteData`\<`TQueryFnData`, `unknown`\>
+
+#### TQueryKey
+
+`TQueryKey` *extends* readonly `unknown`[] = readonly `unknown`[]
+
+#### TPageParam
+
+`TPageParam` = `unknown`
+
+### Parameters
+
+#### options
+
+[`UndefinedInitialDataInfiniteOptions`](../type-aliases/UndefinedInitialDataInfiniteOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\>
+
+### Returns
+
+[`UseInfiniteQueryOptions`](../interfaces/UseInfiniteQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> & `object` & `object`
diff --git a/docs/framework/preact/reference/functions/mutationOptions.md b/docs/framework/preact/reference/functions/mutationOptions.md
new file mode 100644
index 0000000000..85f0d31918
--- /dev/null
+++ b/docs/framework/preact/reference/functions/mutationOptions.md
@@ -0,0 +1,78 @@
+---
+id: mutationOptions
+title: mutationOptions
+---
+
+# Function: mutationOptions()
+
+## Call Signature
+
+```ts
+function mutationOptions(options): WithRequired, "mutationKey">;
+```
+
+Defined in: [preact-query/src/mutationOptions.ts:4](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/mutationOptions.ts#L4)
+
+### Type Parameters
+
+#### TData
+
+`TData` = `unknown`
+
+#### TError
+
+`TError` = `Error`
+
+#### TVariables
+
+`TVariables` = `void`
+
+#### TOnMutateResult
+
+`TOnMutateResult` = `unknown`
+
+### Parameters
+
+#### options
+
+`WithRequired`\<[`UseMutationOptions`](../interfaces/UseMutationOptions.md)\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\>, `"mutationKey"`\>
+
+### Returns
+
+`WithRequired`\<[`UseMutationOptions`](../interfaces/UseMutationOptions.md)\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\>, `"mutationKey"`\>
+
+## Call Signature
+
+```ts
+function mutationOptions(options): Omit, "mutationKey">;
+```
+
+Defined in: [preact-query/src/mutationOptions.ts:18](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/mutationOptions.ts#L18)
+
+### Type Parameters
+
+#### TData
+
+`TData` = `unknown`
+
+#### TError
+
+`TError` = `Error`
+
+#### TVariables
+
+`TVariables` = `void`
+
+#### TOnMutateResult
+
+`TOnMutateResult` = `unknown`
+
+### Parameters
+
+#### options
+
+`Omit`\<[`UseMutationOptions`](../interfaces/UseMutationOptions.md)\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\>, `"mutationKey"`\>
+
+### Returns
+
+`Omit`\<[`UseMutationOptions`](../interfaces/UseMutationOptions.md)\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\>, `"mutationKey"`\>
diff --git a/docs/framework/preact/reference/functions/queryOptions.md b/docs/framework/preact/reference/functions/queryOptions.md
new file mode 100644
index 0000000000..ec10ea91a9
--- /dev/null
+++ b/docs/framework/preact/reference/functions/queryOptions.md
@@ -0,0 +1,114 @@
+---
+id: queryOptions
+title: queryOptions
+---
+
+# Function: queryOptions()
+
+## Call Signature
+
+```ts
+function queryOptions(options): Omit, "queryFn"> & object & object;
+```
+
+Defined in: [preact-query/src/queryOptions.ts:52](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/queryOptions.ts#L52)
+
+### Type Parameters
+
+#### TQueryFnData
+
+`TQueryFnData` = `unknown`
+
+#### TError
+
+`TError` = `Error`
+
+#### TData
+
+`TData` = `TQueryFnData`
+
+#### TQueryKey
+
+`TQueryKey` *extends* readonly `unknown`[] = readonly `unknown`[]
+
+### Parameters
+
+#### options
+
+[`DefinedInitialDataOptions`](../type-aliases/DefinedInitialDataOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>
+
+### Returns
+
+`Omit`\<[`UseQueryOptions`](../interfaces/UseQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>, `"queryFn"`\> & `object` & `object`
+
+## Call Signature
+
+```ts
+function queryOptions(options): OmitKeyof, "queryFn"> & object & object;
+```
+
+Defined in: [preact-query/src/queryOptions.ts:63](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/queryOptions.ts#L63)
+
+### Type Parameters
+
+#### TQueryFnData
+
+`TQueryFnData` = `unknown`
+
+#### TError
+
+`TError` = `Error`
+
+#### TData
+
+`TData` = `TQueryFnData`
+
+#### TQueryKey
+
+`TQueryKey` *extends* readonly `unknown`[] = readonly `unknown`[]
+
+### Parameters
+
+#### options
+
+[`UnusedSkipTokenOptions`](../type-aliases/UnusedSkipTokenOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>
+
+### Returns
+
+`OmitKeyof`\<[`UseQueryOptions`](../interfaces/UseQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>, `"queryFn"`\> & `object` & `object`
+
+## Call Signature
+
+```ts
+function queryOptions(options): UseQueryOptions & object & object;
+```
+
+Defined in: [preact-query/src/queryOptions.ts:74](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/queryOptions.ts#L74)
+
+### Type Parameters
+
+#### TQueryFnData
+
+`TQueryFnData` = `unknown`
+
+#### TError
+
+`TError` = `Error`
+
+#### TData
+
+`TData` = `TQueryFnData`
+
+#### TQueryKey
+
+`TQueryKey` *extends* readonly `unknown`[] = readonly `unknown`[]
+
+### Parameters
+
+#### options
+
+[`UndefinedInitialDataOptions`](../type-aliases/UndefinedInitialDataOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>
+
+### Returns
+
+[`UseQueryOptions`](../interfaces/UseQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> & `object` & `object`
diff --git a/docs/framework/preact/reference/functions/useInfiniteQuery.md b/docs/framework/preact/reference/functions/useInfiniteQuery.md
new file mode 100644
index 0000000000..70f7aa1fdc
--- /dev/null
+++ b/docs/framework/preact/reference/functions/useInfiniteQuery.md
@@ -0,0 +1,138 @@
+---
+id: useInfiniteQuery
+title: useInfiniteQuery
+---
+
+# Function: useInfiniteQuery()
+
+## Call Signature
+
+```ts
+function useInfiniteQuery(options, queryClient?): DefinedUseInfiniteQueryResult;
+```
+
+Defined in: [preact-query/src/useInfiniteQuery.ts:20](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/useInfiniteQuery.ts#L20)
+
+### Type Parameters
+
+#### TQueryFnData
+
+`TQueryFnData`
+
+#### TError
+
+`TError` = `Error`
+
+#### TData
+
+`TData` = `InfiniteData`\<`TQueryFnData`, `unknown`\>
+
+#### TQueryKey
+
+`TQueryKey` *extends* readonly `unknown`[] = readonly `unknown`[]
+
+#### TPageParam
+
+`TPageParam` = `unknown`
+
+### Parameters
+
+#### options
+
+[`DefinedInitialDataInfiniteOptions`](../type-aliases/DefinedInitialDataInfiniteOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\>
+
+#### queryClient?
+
+`QueryClient`
+
+### Returns
+
+[`DefinedUseInfiniteQueryResult`](../type-aliases/DefinedUseInfiniteQueryResult.md)\<`TData`, `TError`\>
+
+## Call Signature
+
+```ts
+function useInfiniteQuery(options, queryClient?): UseInfiniteQueryResult;
+```
+
+Defined in: [preact-query/src/useInfiniteQuery.ts:37](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/useInfiniteQuery.ts#L37)
+
+### Type Parameters
+
+#### TQueryFnData
+
+`TQueryFnData`
+
+#### TError
+
+`TError` = `Error`
+
+#### TData
+
+`TData` = `InfiniteData`\<`TQueryFnData`, `unknown`\>
+
+#### TQueryKey
+
+`TQueryKey` *extends* readonly `unknown`[] = readonly `unknown`[]
+
+#### TPageParam
+
+`TPageParam` = `unknown`
+
+### Parameters
+
+#### options
+
+[`UndefinedInitialDataInfiniteOptions`](../type-aliases/UndefinedInitialDataInfiniteOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\>
+
+#### queryClient?
+
+`QueryClient`
+
+### Returns
+
+[`UseInfiniteQueryResult`](../type-aliases/UseInfiniteQueryResult.md)\<`TData`, `TError`\>
+
+## Call Signature
+
+```ts
+function useInfiniteQuery(options, queryClient?): UseInfiniteQueryResult;
+```
+
+Defined in: [preact-query/src/useInfiniteQuery.ts:54](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/useInfiniteQuery.ts#L54)
+
+### Type Parameters
+
+#### TQueryFnData
+
+`TQueryFnData`
+
+#### TError
+
+`TError` = `Error`
+
+#### TData
+
+`TData` = `InfiniteData`\<`TQueryFnData`, `unknown`\>
+
+#### TQueryKey
+
+`TQueryKey` *extends* readonly `unknown`[] = readonly `unknown`[]
+
+#### TPageParam
+
+`TPageParam` = `unknown`
+
+### Parameters
+
+#### options
+
+[`UseInfiniteQueryOptions`](../interfaces/UseInfiniteQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\>
+
+#### queryClient?
+
+`QueryClient`
+
+### Returns
+
+[`UseInfiniteQueryResult`](../type-aliases/UseInfiniteQueryResult.md)\<`TData`, `TError`\>
diff --git a/docs/framework/preact/reference/functions/useIsFetching.md b/docs/framework/preact/reference/functions/useIsFetching.md
new file mode 100644
index 0000000000..316de0dc15
--- /dev/null
+++ b/docs/framework/preact/reference/functions/useIsFetching.md
@@ -0,0 +1,26 @@
+---
+id: useIsFetching
+title: useIsFetching
+---
+
+# Function: useIsFetching()
+
+```ts
+function useIsFetching(filters?, queryClient?): number;
+```
+
+Defined in: [preact-query/src/useIsFetching.ts:8](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/useIsFetching.ts#L8)
+
+## Parameters
+
+### filters?
+
+`QueryFilters`\
+
+### queryClient?
+
+`QueryClient`
+
+## Returns
+
+`number`
diff --git a/docs/framework/preact/reference/functions/useIsMutating.md b/docs/framework/preact/reference/functions/useIsMutating.md
new file mode 100644
index 0000000000..f0cf11e560
--- /dev/null
+++ b/docs/framework/preact/reference/functions/useIsMutating.md
@@ -0,0 +1,26 @@
+---
+id: useIsMutating
+title: useIsMutating
+---
+
+# Function: useIsMutating()
+
+```ts
+function useIsMutating(filters?, queryClient?): number;
+```
+
+Defined in: [preact-query/src/useMutationState.ts:13](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/useMutationState.ts#L13)
+
+## Parameters
+
+### filters?
+
+`MutationFilters`\<`unknown`, `Error`, `unknown`, `unknown`\>
+
+### queryClient?
+
+`QueryClient`
+
+## Returns
+
+`number`
diff --git a/docs/framework/preact/reference/functions/useIsRestoring.md b/docs/framework/preact/reference/functions/useIsRestoring.md
new file mode 100644
index 0000000000..ade5658b1d
--- /dev/null
+++ b/docs/framework/preact/reference/functions/useIsRestoring.md
@@ -0,0 +1,16 @@
+---
+id: useIsRestoring
+title: useIsRestoring
+---
+
+# Function: useIsRestoring()
+
+```ts
+function useIsRestoring(): boolean;
+```
+
+Defined in: [preact-query/src/IsRestoringProvider.ts:6](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/IsRestoringProvider.ts#L6)
+
+## Returns
+
+`boolean`
diff --git a/docs/framework/preact/reference/functions/useMutation.md b/docs/framework/preact/reference/functions/useMutation.md
new file mode 100644
index 0000000000..e02878a2ba
--- /dev/null
+++ b/docs/framework/preact/reference/functions/useMutation.md
@@ -0,0 +1,44 @@
+---
+id: useMutation
+title: useMutation
+---
+
+# Function: useMutation()
+
+```ts
+function useMutation(options, queryClient?): UseMutationResult;
+```
+
+Defined in: [preact-query/src/useMutation.ts:19](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/useMutation.ts#L19)
+
+## Type Parameters
+
+### TData
+
+`TData` = `unknown`
+
+### TError
+
+`TError` = `Error`
+
+### TVariables
+
+`TVariables` = `void`
+
+### TOnMutateResult
+
+`TOnMutateResult` = `unknown`
+
+## Parameters
+
+### options
+
+[`UseMutationOptions`](../interfaces/UseMutationOptions.md)\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\>
+
+### queryClient?
+
+`QueryClient`
+
+## Returns
+
+[`UseMutationResult`](../type-aliases/UseMutationResult.md)\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\>
diff --git a/docs/framework/preact/reference/functions/useMutationState.md b/docs/framework/preact/reference/functions/useMutationState.md
new file mode 100644
index 0000000000..cdf89e76d5
--- /dev/null
+++ b/docs/framework/preact/reference/functions/useMutationState.md
@@ -0,0 +1,32 @@
+---
+id: useMutationState
+title: useMutationState
+---
+
+# Function: useMutationState()
+
+```ts
+function useMutationState(options, queryClient?): TResult[];
+```
+
+Defined in: [preact-query/src/useMutationState.ts:41](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/useMutationState.ts#L41)
+
+## Type Parameters
+
+### TResult
+
+`TResult` = `MutationState`\<`unknown`, `Error`, `unknown`, `unknown`\>
+
+## Parameters
+
+### options
+
+`MutationStateOptions`\<`TResult`\> = `{}`
+
+### queryClient?
+
+`QueryClient`
+
+## Returns
+
+`TResult`[]
diff --git a/docs/framework/preact/reference/functions/usePrefetchInfiniteQuery.md b/docs/framework/preact/reference/functions/usePrefetchInfiniteQuery.md
new file mode 100644
index 0000000000..b405222a0f
--- /dev/null
+++ b/docs/framework/preact/reference/functions/usePrefetchInfiniteQuery.md
@@ -0,0 +1,48 @@
+---
+id: usePrefetchInfiniteQuery
+title: usePrefetchInfiniteQuery
+---
+
+# Function: usePrefetchInfiniteQuery()
+
+```ts
+function usePrefetchInfiniteQuery(options, queryClient?): void;
+```
+
+Defined in: [preact-query/src/usePrefetchInfiniteQuery.tsx:9](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/usePrefetchInfiniteQuery.tsx#L9)
+
+## Type Parameters
+
+### TQueryFnData
+
+`TQueryFnData` = `unknown`
+
+### TError
+
+`TError` = `Error`
+
+### TData
+
+`TData` = `TQueryFnData`
+
+### TQueryKey
+
+`TQueryKey` *extends* readonly `unknown`[] = readonly `unknown`[]
+
+### TPageParam
+
+`TPageParam` = `unknown`
+
+## Parameters
+
+### options
+
+`FetchInfiniteQueryOptions`\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\>
+
+### queryClient?
+
+`QueryClient`
+
+## Returns
+
+`void`
diff --git a/docs/framework/preact/reference/functions/usePrefetchQuery.md b/docs/framework/preact/reference/functions/usePrefetchQuery.md
new file mode 100644
index 0000000000..dba7adcec5
--- /dev/null
+++ b/docs/framework/preact/reference/functions/usePrefetchQuery.md
@@ -0,0 +1,44 @@
+---
+id: usePrefetchQuery
+title: usePrefetchQuery
+---
+
+# Function: usePrefetchQuery()
+
+```ts
+function usePrefetchQuery(options, queryClient?): void;
+```
+
+Defined in: [preact-query/src/usePrefetchQuery.tsx:5](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/usePrefetchQuery.tsx#L5)
+
+## Type Parameters
+
+### TQueryFnData
+
+`TQueryFnData` = `unknown`
+
+### TError
+
+`TError` = `Error`
+
+### TData
+
+`TData` = `TQueryFnData`
+
+### TQueryKey
+
+`TQueryKey` *extends* readonly `unknown`[] = readonly `unknown`[]
+
+## Parameters
+
+### options
+
+[`UsePrefetchQueryOptions`](../interfaces/UsePrefetchQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>
+
+### queryClient?
+
+`QueryClient`
+
+## Returns
+
+`void`
diff --git a/docs/framework/preact/reference/functions/useQueries.md b/docs/framework/preact/reference/functions/useQueries.md
new file mode 100644
index 0000000000..2fa7bcd80e
--- /dev/null
+++ b/docs/framework/preact/reference/functions/useQueries.md
@@ -0,0 +1,47 @@
+---
+id: useQueries
+title: useQueries
+---
+
+# Function: useQueries()
+
+```ts
+function useQueries(__namedParameters, queryClient?): TCombinedResult;
+```
+
+Defined in: [preact-query/src/useQueries.ts:207](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/useQueries.ts#L207)
+
+## Type Parameters
+
+### T
+
+`T` *extends* `any`[]
+
+### TCombinedResult
+
+`TCombinedResult` = `T` *extends* \[\] ? \[\] : `T` *extends* \[`Head`\] ? \[`GetUseQueryResult`\<`Head`\>\] : `T` *extends* \[`Head`, `...Tails[]`\] ? \[`...Tails[]`\] *extends* \[\] ? \[\] : \[`...Tails[]`\] *extends* \[`Head`\] ? \[`GetUseQueryResult`\<`Head`\>, `GetUseQueryResult`\<`Head`\>\] : \[`...Tails[]`\] *extends* \[`Head`, `...Tails[]`\] ? \[`...Tails[]`\] *extends* \[\] ? \[\] : \[`...Tails[]`\] *extends* \[`Head`\] ? \[`GetUseQueryResult`\<`Head`\>, `GetUseQueryResult`\<`Head`\>, `GetUseQueryResult`\<`Head`\>\] : \[`...Tails[]`\] *extends* \[`Head`, `...Tails[]`\] ? \[`...(...)[]`\] *extends* \[\] ? \[\] : ... *extends* ... ? ... : ... : \[`...{ [K in (...)]: (...) }[]`\] : \[...\{ \[K in string \| number \| symbol\]: GetUseQueryResult\\]\> \}\[\]\] : \{ \[K in string \| number \| symbol\]: GetUseQueryResult\\]\> \}
+
+## Parameters
+
+### \_\_namedParameters
+
+#### combine?
+
+(`result`) => `TCombinedResult`
+
+#### queries
+
+ \| readonly \[`T` *extends* \[\] ? \[\] : `T` *extends* \[`Head`\] ? \[`GetUseQueryOptionsForUseQueries`\<`Head`\>\] : `T` *extends* \[`Head`, `...Tails[]`\] ? \[`...Tails[]`\] *extends* \[\] ? \[\] : \[`...Tails[]`\] *extends* \[`Head`\] ? \[`GetUseQueryOptionsForUseQueries`\<`Head`\>, `GetUseQueryOptionsForUseQueries`\<`Head`\>\] : \[`...Tails[]`\] *extends* \[`Head`, `...Tails[]`\] ? \[`...(...)[]`\] *extends* \[\] ? \[\] : ... *extends* ... ? ... : ... : readonly ...[] *extends* \[`...(...)[]`\] ? \[`...(...)[]`\] : ... *extends* ... ? ... : ... : readonly `unknown`[] *extends* `T` ? `T` : `T` *extends* `UseQueryOptionsForUseQueries`\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>[] ? `UseQueryOptionsForUseQueries`\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>[] : `UseQueryOptionsForUseQueries`\<`unknown`, `Error`, `unknown`, readonly ...[]\>[]\]
+ \| readonly \[\{ \[K in string \| number \| symbol\]: GetUseQueryOptionsForUseQueries\\]\> \}\]
+
+#### subscribed?
+
+`boolean`
+
+### queryClient?
+
+`QueryClient`
+
+## Returns
+
+`TCombinedResult`
diff --git a/docs/framework/preact/reference/functions/useQuery.md b/docs/framework/preact/reference/functions/useQuery.md
new file mode 100644
index 0000000000..6ddf4c31e9
--- /dev/null
+++ b/docs/framework/preact/reference/functions/useQuery.md
@@ -0,0 +1,126 @@
+---
+id: useQuery
+title: useQuery
+---
+
+# Function: useQuery()
+
+## Call Signature
+
+```ts
+function useQuery(options, queryClient?): DefinedUseQueryResult, TError>;
+```
+
+Defined in: [preact-query/src/useQuery.ts:19](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/useQuery.ts#L19)
+
+### Type Parameters
+
+#### TQueryFnData
+
+`TQueryFnData` = `unknown`
+
+#### TError
+
+`TError` = `Error`
+
+#### TData
+
+`TData` = `TQueryFnData`
+
+#### TQueryKey
+
+`TQueryKey` *extends* readonly `unknown`[] = readonly `unknown`[]
+
+### Parameters
+
+#### options
+
+[`DefinedInitialDataOptions`](../type-aliases/DefinedInitialDataOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>
+
+#### queryClient?
+
+`QueryClient`
+
+### Returns
+
+[`DefinedUseQueryResult`](../type-aliases/DefinedUseQueryResult.md)\<`NoInfer`\<`TData`\>, `TError`\>
+
+## Call Signature
+
+```ts
+function useQuery(options, queryClient?): UseQueryResult, TError>;
+```
+
+Defined in: [preact-query/src/useQuery.ts:29](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/useQuery.ts#L29)
+
+### Type Parameters
+
+#### TQueryFnData
+
+`TQueryFnData` = `unknown`
+
+#### TError
+
+`TError` = `Error`
+
+#### TData
+
+`TData` = `TQueryFnData`
+
+#### TQueryKey
+
+`TQueryKey` *extends* readonly `unknown`[] = readonly `unknown`[]
+
+### Parameters
+
+#### options
+
+[`UndefinedInitialDataOptions`](../type-aliases/UndefinedInitialDataOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>
+
+#### queryClient?
+
+`QueryClient`
+
+### Returns
+
+[`UseQueryResult`](../type-aliases/UseQueryResult.md)\<`NoInfer`\<`TData`\>, `TError`\>
+
+## Call Signature
+
+```ts
+function useQuery(options, queryClient?): UseQueryResult, TError>;
+```
+
+Defined in: [preact-query/src/useQuery.ts:39](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/useQuery.ts#L39)
+
+### Type Parameters
+
+#### TQueryFnData
+
+`TQueryFnData` = `unknown`
+
+#### TError
+
+`TError` = `Error`
+
+#### TData
+
+`TData` = `TQueryFnData`
+
+#### TQueryKey
+
+`TQueryKey` *extends* readonly `unknown`[] = readonly `unknown`[]
+
+### Parameters
+
+#### options
+
+[`UseQueryOptions`](../interfaces/UseQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>
+
+#### queryClient?
+
+`QueryClient`
+
+### Returns
+
+[`UseQueryResult`](../type-aliases/UseQueryResult.md)\<`NoInfer`\<`TData`\>, `TError`\>
diff --git a/docs/framework/preact/reference/functions/useQueryClient.md b/docs/framework/preact/reference/functions/useQueryClient.md
new file mode 100644
index 0000000000..ecb5fa2516
--- /dev/null
+++ b/docs/framework/preact/reference/functions/useQueryClient.md
@@ -0,0 +1,22 @@
+---
+id: useQueryClient
+title: useQueryClient
+---
+
+# Function: useQueryClient()
+
+```ts
+function useQueryClient(queryClient?): QueryClient;
+```
+
+Defined in: [preact-query/src/QueryClientProvider.tsx:9](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/QueryClientProvider.tsx#L9)
+
+## Parameters
+
+### queryClient?
+
+`QueryClient`
+
+## Returns
+
+`QueryClient`
diff --git a/docs/framework/preact/reference/functions/useQueryErrorResetBoundary.md b/docs/framework/preact/reference/functions/useQueryErrorResetBoundary.md
new file mode 100644
index 0000000000..560c1f869f
--- /dev/null
+++ b/docs/framework/preact/reference/functions/useQueryErrorResetBoundary.md
@@ -0,0 +1,16 @@
+---
+id: useQueryErrorResetBoundary
+title: useQueryErrorResetBoundary
+---
+
+# Function: useQueryErrorResetBoundary()
+
+```ts
+function useQueryErrorResetBoundary(): QueryErrorResetBoundaryValue;
+```
+
+Defined in: [preact-query/src/QueryErrorResetBoundary.tsx:34](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/QueryErrorResetBoundary.tsx#L34)
+
+## Returns
+
+`QueryErrorResetBoundaryValue`
diff --git a/docs/framework/preact/reference/functions/useSuspenseInfiniteQuery.md b/docs/framework/preact/reference/functions/useSuspenseInfiniteQuery.md
new file mode 100644
index 0000000000..31a2a93fe7
--- /dev/null
+++ b/docs/framework/preact/reference/functions/useSuspenseInfiniteQuery.md
@@ -0,0 +1,48 @@
+---
+id: useSuspenseInfiniteQuery
+title: useSuspenseInfiniteQuery
+---
+
+# Function: useSuspenseInfiniteQuery()
+
+```ts
+function useSuspenseInfiniteQuery(options, queryClient?): UseSuspenseInfiniteQueryResult;
+```
+
+Defined in: [preact-query/src/useSuspenseInfiniteQuery.ts:17](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/useSuspenseInfiniteQuery.ts#L17)
+
+## Type Parameters
+
+### TQueryFnData
+
+`TQueryFnData`
+
+### TError
+
+`TError` = `Error`
+
+### TData
+
+`TData` = `InfiniteData`\<`TQueryFnData`, `unknown`\>
+
+### TQueryKey
+
+`TQueryKey` *extends* readonly `unknown`[] = readonly `unknown`[]
+
+### TPageParam
+
+`TPageParam` = `unknown`
+
+## Parameters
+
+### options
+
+[`UseSuspenseInfiniteQueryOptions`](../interfaces/UseSuspenseInfiniteQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\>
+
+### queryClient?
+
+`QueryClient`
+
+## Returns
+
+[`UseSuspenseInfiniteQueryResult`](../type-aliases/UseSuspenseInfiniteQueryResult.md)\<`TData`, `TError`\>
diff --git a/docs/framework/preact/reference/functions/useSuspenseQueries.md b/docs/framework/preact/reference/functions/useSuspenseQueries.md
new file mode 100644
index 0000000000..cd60a3bee2
--- /dev/null
+++ b/docs/framework/preact/reference/functions/useSuspenseQueries.md
@@ -0,0 +1,83 @@
+---
+id: useSuspenseQueries
+title: useSuspenseQueries
+---
+
+# Function: useSuspenseQueries()
+
+## Call Signature
+
+```ts
+function useSuspenseQueries(options, queryClient?): TCombinedResult;
+```
+
+Defined in: [preact-query/src/useSuspenseQueries.ts:164](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/useSuspenseQueries.ts#L164)
+
+### Type Parameters
+
+#### T
+
+`T` *extends* `any`[]
+
+#### TCombinedResult
+
+`TCombinedResult` = `T` *extends* \[\] ? \[\] : `T` *extends* \[`Head`\] ? \[`GetUseSuspenseQueryResult`\<`Head`\>\] : `T` *extends* \[`Head`, `...Tails[]`\] ? \[`...Tails[]`\] *extends* \[\] ? \[\] : \[`...Tails[]`\] *extends* \[`Head`\] ? \[`GetUseSuspenseQueryResult`\<`Head`\>, `GetUseSuspenseQueryResult`\<`Head`\>\] : \[`...Tails[]`\] *extends* \[`Head`, `...Tails[]`\] ? \[`...Tails[]`\] *extends* \[\] ? \[\] : \[`...Tails[]`\] *extends* \[`Head`\] ? \[`GetUseSuspenseQueryResult`\<`Head`\>, `GetUseSuspenseQueryResult`\<`Head`\>, `GetUseSuspenseQueryResult`\<`Head`\>\] : \[`...Tails[]`\] *extends* \[`Head`, `...Tails[]`\] ? \[`...(...)[]`\] *extends* \[\] ? \[\] : ... *extends* ... ? ... : ... : \[`...{ [K in (...)]: (...) }[]`\] : \[...\{ \[K in string \| number \| symbol\]: GetUseSuspenseQueryResult\\]\> \}\[\]\] : \{ \[K in string \| number \| symbol\]: GetUseSuspenseQueryResult\\]\> \}
+
+### Parameters
+
+#### options
+
+##### combine?
+
+(`result`) => `TCombinedResult`
+
+##### queries
+
+ \| readonly \[`T` *extends* \[\] ? \[\] : `T` *extends* \[`Head`\] ? \[`GetUseSuspenseQueryOptions`\<`Head`\>\] : `T` *extends* \[`Head`, `...Tails[]`\] ? \[`...Tails[]`\] *extends* \[\] ? \[\] : \[`...Tails[]`\] *extends* \[`Head`\] ? \[`GetUseSuspenseQueryOptions`\<`Head`\>, `GetUseSuspenseQueryOptions`\<`Head`\>\] : \[`...Tails[]`\] *extends* \[`Head`, `...Tails[]`\] ? \[`...(...)[]`\] *extends* \[\] ? \[\] : ... *extends* ... ? ... : ... : ...[] *extends* \[`...(...)[]`\] ? \[`...(...)[]`\] : ... *extends* ... ? ... : ... : `unknown`[] *extends* `T` ? `T` : `T` *extends* [`UseSuspenseQueryOptions`](../interfaces/UseSuspenseQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>[] ? [`UseSuspenseQueryOptions`](../interfaces/UseSuspenseQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>[] : [`UseSuspenseQueryOptions`](../interfaces/UseSuspenseQueryOptions.md)\<`unknown`, `Error`, `unknown`, readonly ...[]\>[]\]
+ \| readonly \[\{ \[K in string \| number \| symbol\]: GetUseSuspenseQueryOptions\\]\> \}\]
+
+#### queryClient?
+
+`QueryClient`
+
+### Returns
+
+`TCombinedResult`
+
+## Call Signature
+
+```ts
+function useSuspenseQueries(options, queryClient?): TCombinedResult;
+```
+
+Defined in: [preact-query/src/useSuspenseQueries.ts:177](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/useSuspenseQueries.ts#L177)
+
+### Type Parameters
+
+#### T
+
+`T` *extends* `any`[]
+
+#### TCombinedResult
+
+`TCombinedResult` = `T` *extends* \[\] ? \[\] : `T` *extends* \[`Head`\] ? \[`GetUseSuspenseQueryResult`\<`Head`\>\] : `T` *extends* \[`Head`, `...Tails[]`\] ? \[`...Tails[]`\] *extends* \[\] ? \[\] : \[`...Tails[]`\] *extends* \[`Head`\] ? \[`GetUseSuspenseQueryResult`\<`Head`\>, `GetUseSuspenseQueryResult`\<`Head`\>\] : \[`...Tails[]`\] *extends* \[`Head`, `...Tails[]`\] ? \[`...Tails[]`\] *extends* \[\] ? \[\] : \[`...Tails[]`\] *extends* \[`Head`\] ? \[`GetUseSuspenseQueryResult`\<`Head`\>, `GetUseSuspenseQueryResult`\<`Head`\>, `GetUseSuspenseQueryResult`\<`Head`\>\] : \[`...Tails[]`\] *extends* \[`Head`, `...Tails[]`\] ? \[`...(...)[]`\] *extends* \[\] ? \[\] : ... *extends* ... ? ... : ... : \[`...{ [K in (...)]: (...) }[]`\] : \[...\{ \[K in string \| number \| symbol\]: GetUseSuspenseQueryResult\\]\> \}\[\]\] : \{ \[K in string \| number \| symbol\]: GetUseSuspenseQueryResult\\]\> \}
+
+### Parameters
+
+#### options
+
+##### combine?
+
+(`result`) => `TCombinedResult`
+
+##### queries
+
+readonly \[`T` *extends* \[\] ? \[\] : `T` *extends* \[`Head`\] ? \[`GetUseSuspenseQueryOptions`\<`Head`\>\] : `T` *extends* \[`Head`, `...Tails[]`\] ? \[`...Tails[]`\] *extends* \[\] ? \[\] : \[`...Tails[]`\] *extends* \[`Head`\] ? \[`GetUseSuspenseQueryOptions`\<`Head`\>, `GetUseSuspenseQueryOptions`\<`Head`\>\] : \[`...Tails[]`\] *extends* \[`Head`, `...Tails[]`\] ? \[`...Tails[]`\] *extends* \[\] ? \[\] : \[`...(...)[]`\] *extends* \[...\] ? \[..., ..., ...\] : ... *extends* ... ? ... : ... : `unknown`[] *extends* \[`...Tails[]`\] ? \[`...Tails[]`\] : \[`...(...)[]`\] *extends* ...[] ? ...[] : ...[] : `unknown`[] *extends* `T` ? `T` : `T` *extends* [`UseSuspenseQueryOptions`](../interfaces/UseSuspenseQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>[] ? [`UseSuspenseQueryOptions`](../interfaces/UseSuspenseQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>[] : [`UseSuspenseQueryOptions`](../interfaces/UseSuspenseQueryOptions.md)\<`unknown`, `Error`, `unknown`, readonly `unknown`[]\>[]\]
+
+#### queryClient?
+
+`QueryClient`
+
+### Returns
+
+`TCombinedResult`
diff --git a/docs/framework/preact/reference/functions/useSuspenseQuery.md b/docs/framework/preact/reference/functions/useSuspenseQuery.md
new file mode 100644
index 0000000000..c97c0185bf
--- /dev/null
+++ b/docs/framework/preact/reference/functions/useSuspenseQuery.md
@@ -0,0 +1,44 @@
+---
+id: useSuspenseQuery
+title: useSuspenseQuery
+---
+
+# Function: useSuspenseQuery()
+
+```ts
+function useSuspenseQuery(options, queryClient?): UseSuspenseQueryResult;
+```
+
+Defined in: [preact-query/src/useSuspenseQuery.ts:7](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/useSuspenseQuery.ts#L7)
+
+## Type Parameters
+
+### TQueryFnData
+
+`TQueryFnData` = `unknown`
+
+### TError
+
+`TError` = `Error`
+
+### TData
+
+`TData` = `TQueryFnData`
+
+### TQueryKey
+
+`TQueryKey` *extends* readonly `unknown`[] = readonly `unknown`[]
+
+## Parameters
+
+### options
+
+[`UseSuspenseQueryOptions`](../interfaces/UseSuspenseQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>
+
+### queryClient?
+
+`QueryClient`
+
+## Returns
+
+[`UseSuspenseQueryResult`](../type-aliases/UseSuspenseQueryResult.md)\<`TData`, `TError`\>
diff --git a/docs/framework/preact/reference/index.md b/docs/framework/preact/reference/index.md
new file mode 100644
index 0000000000..cac27f1e44
--- /dev/null
+++ b/docs/framework/preact/reference/index.md
@@ -0,0 +1,82 @@
+---
+id: "@tanstack/preact-query"
+title: "@tanstack/preact-query"
+---
+
+# @tanstack/preact-query
+
+## Interfaces
+
+- [HydrationBoundaryProps](interfaces/HydrationBoundaryProps.md)
+- [QueryErrorResetBoundaryProps](interfaces/QueryErrorResetBoundaryProps.md)
+- [UseBaseQueryOptions](interfaces/UseBaseQueryOptions.md)
+- [UseInfiniteQueryOptions](interfaces/UseInfiniteQueryOptions.md)
+- [UseMutationOptions](interfaces/UseMutationOptions.md)
+- [UsePrefetchQueryOptions](interfaces/UsePrefetchQueryOptions.md)
+- [UseQueryOptions](interfaces/UseQueryOptions.md)
+- [UseSuspenseInfiniteQueryOptions](interfaces/UseSuspenseInfiniteQueryOptions.md)
+- [UseSuspenseQueryOptions](interfaces/UseSuspenseQueryOptions.md)
+
+## Type Aliases
+
+- [AnyUseBaseQueryOptions](type-aliases/AnyUseBaseQueryOptions.md)
+- [AnyUseInfiniteQueryOptions](type-aliases/AnyUseInfiniteQueryOptions.md)
+- [AnyUseMutationOptions](type-aliases/AnyUseMutationOptions.md)
+- [AnyUseQueryOptions](type-aliases/AnyUseQueryOptions.md)
+- [AnyUseSuspenseInfiniteQueryOptions](type-aliases/AnyUseSuspenseInfiniteQueryOptions.md)
+- [AnyUseSuspenseQueryOptions](type-aliases/AnyUseSuspenseQueryOptions.md)
+- [DefinedInitialDataInfiniteOptions](type-aliases/DefinedInitialDataInfiniteOptions.md)
+- [DefinedInitialDataOptions](type-aliases/DefinedInitialDataOptions.md)
+- [DefinedUseInfiniteQueryResult](type-aliases/DefinedUseInfiniteQueryResult.md)
+- [DefinedUseQueryResult](type-aliases/DefinedUseQueryResult.md)
+- [QueriesOptions](type-aliases/QueriesOptions.md)
+- [QueriesResults](type-aliases/QueriesResults.md)
+- [QueryClientProviderProps](type-aliases/QueryClientProviderProps.md)
+- [QueryErrorClearResetFunction](type-aliases/QueryErrorClearResetFunction.md)
+- [QueryErrorIsResetFunction](type-aliases/QueryErrorIsResetFunction.md)
+- [QueryErrorResetBoundaryFunction](type-aliases/QueryErrorResetBoundaryFunction.md)
+- [QueryErrorResetFunction](type-aliases/QueryErrorResetFunction.md)
+- [SuspenseQueriesOptions](type-aliases/SuspenseQueriesOptions.md)
+- [SuspenseQueriesResults](type-aliases/SuspenseQueriesResults.md)
+- [UndefinedInitialDataInfiniteOptions](type-aliases/UndefinedInitialDataInfiniteOptions.md)
+- [UndefinedInitialDataOptions](type-aliases/UndefinedInitialDataOptions.md)
+- [UnusedSkipTokenInfiniteOptions](type-aliases/UnusedSkipTokenInfiniteOptions.md)
+- [UnusedSkipTokenOptions](type-aliases/UnusedSkipTokenOptions.md)
+- [UseBaseMutationResult](type-aliases/UseBaseMutationResult.md)
+- [UseBaseQueryResult](type-aliases/UseBaseQueryResult.md)
+- [UseInfiniteQueryResult](type-aliases/UseInfiniteQueryResult.md)
+- [UseMutateAsyncFunction](type-aliases/UseMutateAsyncFunction.md)
+- [UseMutateFunction](type-aliases/UseMutateFunction.md)
+- [UseMutationResult](type-aliases/UseMutationResult.md)
+- [UseQueryResult](type-aliases/UseQueryResult.md)
+- [UseSuspenseInfiniteQueryResult](type-aliases/UseSuspenseInfiniteQueryResult.md)
+- [UseSuspenseQueryResult](type-aliases/UseSuspenseQueryResult.md)
+
+## Variables
+
+- [IsRestoringProvider](variables/IsRestoringProvider.md)
+- [QueryClientContext](variables/QueryClientContext.md)
+
+## Functions
+
+- [HydrationBoundary](functions/HydrationBoundary.md)
+- [infiniteQueryOptions](functions/infiniteQueryOptions.md)
+- [mutationOptions](functions/mutationOptions.md)
+- [QueryClientProvider](functions/QueryClientProvider.md)
+- [QueryErrorResetBoundary](functions/QueryErrorResetBoundary.md)
+- [queryOptions](functions/queryOptions.md)
+- [useInfiniteQuery](functions/useInfiniteQuery.md)
+- [useIsFetching](functions/useIsFetching.md)
+- [useIsMutating](functions/useIsMutating.md)
+- [useIsRestoring](functions/useIsRestoring.md)
+- [useMutation](functions/useMutation.md)
+- [useMutationState](functions/useMutationState.md)
+- [usePrefetchInfiniteQuery](functions/usePrefetchInfiniteQuery.md)
+- [usePrefetchQuery](functions/usePrefetchQuery.md)
+- [useQueries](functions/useQueries.md)
+- [useQuery](functions/useQuery.md)
+- [useQueryClient](functions/useQueryClient.md)
+- [useQueryErrorResetBoundary](functions/useQueryErrorResetBoundary.md)
+- [useSuspenseInfiniteQuery](functions/useSuspenseInfiniteQuery.md)
+- [useSuspenseQueries](functions/useSuspenseQueries.md)
+- [useSuspenseQuery](functions/useSuspenseQuery.md)
diff --git a/docs/framework/preact/reference/interfaces/HydrationBoundaryProps.md b/docs/framework/preact/reference/interfaces/HydrationBoundaryProps.md
new file mode 100644
index 0000000000..96580e8003
--- /dev/null
+++ b/docs/framework/preact/reference/interfaces/HydrationBoundaryProps.md
@@ -0,0 +1,57 @@
+---
+id: HydrationBoundaryProps
+title: HydrationBoundaryProps
+---
+
+# Interface: HydrationBoundaryProps
+
+Defined in: [preact-query/src/HydrationBoundary.tsx:12](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/HydrationBoundary.tsx#L12)
+
+## Properties
+
+### children?
+
+```ts
+optional children: ComponentChildren;
+```
+
+Defined in: [preact-query/src/HydrationBoundary.tsx:20](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/HydrationBoundary.tsx#L20)
+
+***
+
+### options?
+
+```ts
+optional options: OmitKeyof & object;
+```
+
+Defined in: [preact-query/src/HydrationBoundary.tsx:14](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/HydrationBoundary.tsx#L14)
+
+#### Type Declaration
+
+##### defaultOptions?
+
+```ts
+optional defaultOptions: OmitKeyof<{
+}, "mutations">;
+```
+
+***
+
+### queryClient?
+
+```ts
+optional queryClient: QueryClient;
+```
+
+Defined in: [preact-query/src/HydrationBoundary.tsx:21](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/HydrationBoundary.tsx#L21)
+
+***
+
+### state
+
+```ts
+state: DehydratedState | null | undefined;
+```
+
+Defined in: [preact-query/src/HydrationBoundary.tsx:13](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/HydrationBoundary.tsx#L13)
diff --git a/docs/framework/preact/reference/interfaces/QueryErrorResetBoundaryProps.md b/docs/framework/preact/reference/interfaces/QueryErrorResetBoundaryProps.md
new file mode 100644
index 0000000000..265a02e2c8
--- /dev/null
+++ b/docs/framework/preact/reference/interfaces/QueryErrorResetBoundaryProps.md
@@ -0,0 +1,20 @@
+---
+id: QueryErrorResetBoundaryProps
+title: QueryErrorResetBoundaryProps
+---
+
+# Interface: QueryErrorResetBoundaryProps
+
+Defined in: [preact-query/src/QueryErrorResetBoundary.tsx:43](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/QueryErrorResetBoundary.tsx#L43)
+
+## Properties
+
+### children
+
+```ts
+children:
+ | ComponentChildren
+ | QueryErrorResetBoundaryFunction;
+```
+
+Defined in: [preact-query/src/QueryErrorResetBoundary.tsx:44](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/QueryErrorResetBoundary.tsx#L44)
diff --git a/docs/framework/preact/reference/interfaces/UseBaseQueryOptions.md b/docs/framework/preact/reference/interfaces/UseBaseQueryOptions.md
new file mode 100644
index 0000000000..4cf82ebbfa
--- /dev/null
+++ b/docs/framework/preact/reference/interfaces/UseBaseQueryOptions.md
@@ -0,0 +1,47 @@
+---
+id: UseBaseQueryOptions
+title: UseBaseQueryOptions
+---
+
+# Interface: UseBaseQueryOptions\
+
+Defined in: [preact-query/src/types.ts:29](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L29)
+
+## Extends
+
+- `QueryObserverOptions`\<`TQueryFnData`, `TError`, `TData`, `TQueryData`, `TQueryKey`\>
+
+## Type Parameters
+
+### TQueryFnData
+
+`TQueryFnData` = `unknown`
+
+### TError
+
+`TError` = `DefaultError`
+
+### TData
+
+`TData` = `TQueryFnData`
+
+### TQueryData
+
+`TQueryData` = `TQueryFnData`
+
+### TQueryKey
+
+`TQueryKey` *extends* `QueryKey` = `QueryKey`
+
+## Properties
+
+### subscribed?
+
+```ts
+optional subscribed: boolean;
+```
+
+Defined in: [preact-query/src/types.ts:46](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L46)
+
+Set this to `false` to unsubscribe this observer from updates to the query cache.
+Defaults to `true`.
diff --git a/docs/framework/preact/reference/interfaces/UseInfiniteQueryOptions.md b/docs/framework/preact/reference/interfaces/UseInfiniteQueryOptions.md
new file mode 100644
index 0000000000..c7916e2f1d
--- /dev/null
+++ b/docs/framework/preact/reference/interfaces/UseInfiniteQueryOptions.md
@@ -0,0 +1,47 @@
+---
+id: UseInfiniteQueryOptions
+title: UseInfiniteQueryOptions
+---
+
+# Interface: UseInfiniteQueryOptions\
+
+Defined in: [preact-query/src/types.ts:103](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L103)
+
+## Extends
+
+- `OmitKeyof`\<`InfiniteQueryObserverOptions`\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\>, `"suspense"`\>
+
+## Type Parameters
+
+### TQueryFnData
+
+`TQueryFnData` = `unknown`
+
+### TError
+
+`TError` = `DefaultError`
+
+### TData
+
+`TData` = `TQueryFnData`
+
+### TQueryKey
+
+`TQueryKey` *extends* `QueryKey` = `QueryKey`
+
+### TPageParam
+
+`TPageParam` = `unknown`
+
+## Properties
+
+### subscribed?
+
+```ts
+optional subscribed: boolean;
+```
+
+Defined in: [preact-query/src/types.ts:123](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L123)
+
+Set this to `false` to unsubscribe this observer from updates to the query cache.
+Defaults to `true`.
diff --git a/docs/framework/preact/reference/interfaces/UseMutationOptions.md b/docs/framework/preact/reference/interfaces/UseMutationOptions.md
new file mode 100644
index 0000000000..a65643bb16
--- /dev/null
+++ b/docs/framework/preact/reference/interfaces/UseMutationOptions.md
@@ -0,0 +1,30 @@
+---
+id: UseMutationOptions
+title: UseMutationOptions
+---
+
+# Interface: UseMutationOptions\
+
+Defined in: [preact-query/src/types.ts:192](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L192)
+
+## Extends
+
+- `OmitKeyof`\<`MutationObserverOptions`\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\>, `"_defaulted"`\>
+
+## Type Parameters
+
+### TData
+
+`TData` = `unknown`
+
+### TError
+
+`TError` = `DefaultError`
+
+### TVariables
+
+`TVariables` = `void`
+
+### TOnMutateResult
+
+`TOnMutateResult` = `unknown`
diff --git a/docs/framework/preact/reference/interfaces/UsePrefetchQueryOptions.md b/docs/framework/preact/reference/interfaces/UsePrefetchQueryOptions.md
new file mode 100644
index 0000000000..0bbb4a7030
--- /dev/null
+++ b/docs/framework/preact/reference/interfaces/UsePrefetchQueryOptions.md
@@ -0,0 +1,40 @@
+---
+id: UsePrefetchQueryOptions
+title: UsePrefetchQueryOptions
+---
+
+# Interface: UsePrefetchQueryOptions\
+
+Defined in: [preact-query/src/types.ts:49](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L49)
+
+## Extends
+
+- `OmitKeyof`\<`FetchQueryOptions`\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>, `"queryFn"`\>
+
+## Type Parameters
+
+### TQueryFnData
+
+`TQueryFnData` = `unknown`
+
+### TError
+
+`TError` = `DefaultError`
+
+### TData
+
+`TData` = `TQueryFnData`
+
+### TQueryKey
+
+`TQueryKey` *extends* `QueryKey` = `QueryKey`
+
+## Properties
+
+### queryFn?
+
+```ts
+optional queryFn: QueryFunction;
+```
+
+Defined in: [preact-query/src/types.ts:58](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L58)
diff --git a/docs/framework/preact/reference/interfaces/UseQueryOptions.md b/docs/framework/preact/reference/interfaces/UseQueryOptions.md
new file mode 100644
index 0000000000..7bc40d3822
--- /dev/null
+++ b/docs/framework/preact/reference/interfaces/UseQueryOptions.md
@@ -0,0 +1,49 @@
+---
+id: UseQueryOptions
+title: UseQueryOptions
+---
+
+# Interface: UseQueryOptions\
+
+Defined in: [preact-query/src/types.ts:65](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L65)
+
+## Extends
+
+- `OmitKeyof`\<[`UseBaseQueryOptions`](UseBaseQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryFnData`, `TQueryKey`\>, `"suspense"`\>
+
+## Type Parameters
+
+### TQueryFnData
+
+`TQueryFnData` = `unknown`
+
+### TError
+
+`TError` = `DefaultError`
+
+### TData
+
+`TData` = `TQueryFnData`
+
+### TQueryKey
+
+`TQueryKey` *extends* `QueryKey` = `QueryKey`
+
+## Properties
+
+### subscribed?
+
+```ts
+optional subscribed: boolean;
+```
+
+Defined in: [preact-query/src/types.ts:46](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L46)
+
+Set this to `false` to unsubscribe this observer from updates to the query cache.
+Defaults to `true`.
+
+#### Inherited from
+
+```ts
+OmitKeyof.subscribed
+```
diff --git a/docs/framework/preact/reference/interfaces/UseSuspenseInfiniteQueryOptions.md b/docs/framework/preact/reference/interfaces/UseSuspenseInfiniteQueryOptions.md
new file mode 100644
index 0000000000..93c7fbf4fe
--- /dev/null
+++ b/docs/framework/preact/reference/interfaces/UseSuspenseInfiniteQueryOptions.md
@@ -0,0 +1,63 @@
+---
+id: UseSuspenseInfiniteQueryOptions
+title: UseSuspenseInfiniteQueryOptions
+---
+
+# Interface: UseSuspenseInfiniteQueryOptions\
+
+Defined in: [preact-query/src/types.ts:128](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L128)
+
+## Extends
+
+- `OmitKeyof`\<[`UseInfiniteQueryOptions`](UseInfiniteQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\>, `"queryFn"` \| `"enabled"` \| `"throwOnError"` \| `"placeholderData"`\>
+
+## Type Parameters
+
+### TQueryFnData
+
+`TQueryFnData` = `unknown`
+
+### TError
+
+`TError` = `DefaultError`
+
+### TData
+
+`TData` = `TQueryFnData`
+
+### TQueryKey
+
+`TQueryKey` *extends* `QueryKey` = `QueryKey`
+
+### TPageParam
+
+`TPageParam` = `unknown`
+
+## Properties
+
+### queryFn?
+
+```ts
+optional queryFn: QueryFunction;
+```
+
+Defined in: [preact-query/src/types.ts:138](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L138)
+
+***
+
+### subscribed?
+
+```ts
+optional subscribed: boolean;
+```
+
+Defined in: [preact-query/src/types.ts:123](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L123)
+
+Set this to `false` to unsubscribe this observer from updates to the query cache.
+Defaults to `true`.
+
+#### Inherited from
+
+```ts
+OmitKeyof.subscribed
+```
diff --git a/docs/framework/preact/reference/interfaces/UseSuspenseQueryOptions.md b/docs/framework/preact/reference/interfaces/UseSuspenseQueryOptions.md
new file mode 100644
index 0000000000..0db70fe7b2
--- /dev/null
+++ b/docs/framework/preact/reference/interfaces/UseSuspenseQueryOptions.md
@@ -0,0 +1,59 @@
+---
+id: UseSuspenseQueryOptions
+title: UseSuspenseQueryOptions
+---
+
+# Interface: UseSuspenseQueryOptions\
+
+Defined in: [preact-query/src/types.ts:81](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L81)
+
+## Extends
+
+- `OmitKeyof`\<[`UseQueryOptions`](UseQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>, `"queryFn"` \| `"enabled"` \| `"throwOnError"` \| `"placeholderData"`\>
+
+## Type Parameters
+
+### TQueryFnData
+
+`TQueryFnData` = `unknown`
+
+### TError
+
+`TError` = `DefaultError`
+
+### TData
+
+`TData` = `TQueryFnData`
+
+### TQueryKey
+
+`TQueryKey` *extends* `QueryKey` = `QueryKey`
+
+## Properties
+
+### queryFn?
+
+```ts
+optional queryFn: QueryFunction;
+```
+
+Defined in: [preact-query/src/types.ts:90](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L90)
+
+***
+
+### subscribed?
+
+```ts
+optional subscribed: boolean;
+```
+
+Defined in: [preact-query/src/types.ts:46](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L46)
+
+Set this to `false` to unsubscribe this observer from updates to the query cache.
+Defaults to `true`.
+
+#### Inherited from
+
+```ts
+OmitKeyof.subscribed
+```
diff --git a/docs/framework/preact/reference/type-aliases/AnyUseBaseQueryOptions.md b/docs/framework/preact/reference/type-aliases/AnyUseBaseQueryOptions.md
new file mode 100644
index 0000000000..d3ec712209
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/AnyUseBaseQueryOptions.md
@@ -0,0 +1,12 @@
+---
+id: AnyUseBaseQueryOptions
+title: AnyUseBaseQueryOptions
+---
+
+# Type Alias: AnyUseBaseQueryOptions
+
+```ts
+type AnyUseBaseQueryOptions = UseBaseQueryOptions;
+```
+
+Defined in: [preact-query/src/types.ts:22](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L22)
diff --git a/docs/framework/preact/reference/type-aliases/AnyUseInfiniteQueryOptions.md b/docs/framework/preact/reference/type-aliases/AnyUseInfiniteQueryOptions.md
new file mode 100644
index 0000000000..55279e7296
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/AnyUseInfiniteQueryOptions.md
@@ -0,0 +1,12 @@
+---
+id: AnyUseInfiniteQueryOptions
+title: AnyUseInfiniteQueryOptions
+---
+
+# Type Alias: AnyUseInfiniteQueryOptions
+
+```ts
+type AnyUseInfiniteQueryOptions = UseInfiniteQueryOptions;
+```
+
+Defined in: [preact-query/src/types.ts:96](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L96)
diff --git a/docs/framework/preact/reference/type-aliases/AnyUseMutationOptions.md b/docs/framework/preact/reference/type-aliases/AnyUseMutationOptions.md
new file mode 100644
index 0000000000..d4c0e48893
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/AnyUseMutationOptions.md
@@ -0,0 +1,12 @@
+---
+id: AnyUseMutationOptions
+title: AnyUseMutationOptions
+---
+
+# Type Alias: AnyUseMutationOptions
+
+```ts
+type AnyUseMutationOptions = UseMutationOptions;
+```
+
+Defined in: [preact-query/src/types.ts:191](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L191)
diff --git a/docs/framework/preact/reference/type-aliases/AnyUseQueryOptions.md b/docs/framework/preact/reference/type-aliases/AnyUseQueryOptions.md
new file mode 100644
index 0000000000..2af7a0f4b6
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/AnyUseQueryOptions.md
@@ -0,0 +1,12 @@
+---
+id: AnyUseQueryOptions
+title: AnyUseQueryOptions
+---
+
+# Type Alias: AnyUseQueryOptions
+
+```ts
+type AnyUseQueryOptions = UseQueryOptions;
+```
+
+Defined in: [preact-query/src/types.ts:64](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L64)
diff --git a/docs/framework/preact/reference/type-aliases/AnyUseSuspenseInfiniteQueryOptions.md b/docs/framework/preact/reference/type-aliases/AnyUseSuspenseInfiniteQueryOptions.md
new file mode 100644
index 0000000000..3c167849ee
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/AnyUseSuspenseInfiniteQueryOptions.md
@@ -0,0 +1,12 @@
+---
+id: AnyUseSuspenseInfiniteQueryOptions
+title: AnyUseSuspenseInfiniteQueryOptions
+---
+
+# Type Alias: AnyUseSuspenseInfiniteQueryOptions
+
+```ts
+type AnyUseSuspenseInfiniteQueryOptions = UseSuspenseInfiniteQueryOptions;
+```
+
+Defined in: [preact-query/src/types.ts:126](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L126)
diff --git a/docs/framework/preact/reference/type-aliases/AnyUseSuspenseQueryOptions.md b/docs/framework/preact/reference/type-aliases/AnyUseSuspenseQueryOptions.md
new file mode 100644
index 0000000000..673df04a98
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/AnyUseSuspenseQueryOptions.md
@@ -0,0 +1,12 @@
+---
+id: AnyUseSuspenseQueryOptions
+title: AnyUseSuspenseQueryOptions
+---
+
+# Type Alias: AnyUseSuspenseQueryOptions
+
+```ts
+type AnyUseSuspenseQueryOptions = UseSuspenseQueryOptions;
+```
+
+Defined in: [preact-query/src/types.ts:75](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L75)
diff --git a/docs/framework/preact/reference/type-aliases/DefinedInitialDataInfiniteOptions.md b/docs/framework/preact/reference/type-aliases/DefinedInitialDataInfiniteOptions.md
new file mode 100644
index 0000000000..e7166306ba
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/DefinedInitialDataInfiniteOptions.md
@@ -0,0 +1,45 @@
+---
+id: DefinedInitialDataInfiniteOptions
+title: DefinedInitialDataInfiniteOptions
+---
+
+# Type Alias: DefinedInitialDataInfiniteOptions\
+
+```ts
+type DefinedInitialDataInfiniteOptions = UseInfiniteQueryOptions & object;
+```
+
+Defined in: [preact-query/src/infiniteQueryOptions.ts:56](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/infiniteQueryOptions.ts#L56)
+
+## Type Declaration
+
+### initialData
+
+```ts
+initialData:
+ | NonUndefinedGuard>
+ | () => NonUndefinedGuard>
+ | undefined;
+```
+
+## Type Parameters
+
+### TQueryFnData
+
+`TQueryFnData`
+
+### TError
+
+`TError` = `DefaultError`
+
+### TData
+
+`TData` = `InfiniteData`\<`TQueryFnData`\>
+
+### TQueryKey
+
+`TQueryKey` *extends* `QueryKey` = `QueryKey`
+
+### TPageParam
+
+`TPageParam` = `unknown`
diff --git a/docs/framework/preact/reference/type-aliases/DefinedInitialDataOptions.md b/docs/framework/preact/reference/type-aliases/DefinedInitialDataOptions.md
new file mode 100644
index 0000000000..f154f32363
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/DefinedInitialDataOptions.md
@@ -0,0 +1,46 @@
+---
+id: DefinedInitialDataOptions
+title: DefinedInitialDataOptions
+---
+
+# Type Alias: DefinedInitialDataOptions\
+
+```ts
+type DefinedInitialDataOptions = Omit, "queryFn"> & object;
+```
+
+Defined in: [preact-query/src/queryOptions.ts:40](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/queryOptions.ts#L40)
+
+## Type Declaration
+
+### initialData
+
+```ts
+initialData:
+ | NonUndefinedGuard
+| () => NonUndefinedGuard;
+```
+
+### queryFn?
+
+```ts
+optional queryFn: QueryFunction;
+```
+
+## Type Parameters
+
+### TQueryFnData
+
+`TQueryFnData` = `unknown`
+
+### TError
+
+`TError` = `DefaultError`
+
+### TData
+
+`TData` = `TQueryFnData`
+
+### TQueryKey
+
+`TQueryKey` *extends* `QueryKey` = `QueryKey`
diff --git a/docs/framework/preact/reference/type-aliases/DefinedUseInfiniteQueryResult.md b/docs/framework/preact/reference/type-aliases/DefinedUseInfiniteQueryResult.md
new file mode 100644
index 0000000000..a298d4ade0
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/DefinedUseInfiniteQueryResult.md
@@ -0,0 +1,22 @@
+---
+id: DefinedUseInfiniteQueryResult
+title: DefinedUseInfiniteQueryResult
+---
+
+# Type Alias: DefinedUseInfiniteQueryResult\
+
+```ts
+type DefinedUseInfiniteQueryResult = DefinedInfiniteQueryObserverResult;
+```
+
+Defined in: [preact-query/src/types.ts:178](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L178)
+
+## Type Parameters
+
+### TData
+
+`TData` = `unknown`
+
+### TError
+
+`TError` = `DefaultError`
diff --git a/docs/framework/preact/reference/type-aliases/DefinedUseQueryResult.md b/docs/framework/preact/reference/type-aliases/DefinedUseQueryResult.md
new file mode 100644
index 0000000000..e9a1112cb4
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/DefinedUseQueryResult.md
@@ -0,0 +1,22 @@
+---
+id: DefinedUseQueryResult
+title: DefinedUseQueryResult
+---
+
+# Type Alias: DefinedUseQueryResult\
+
+```ts
+type DefinedUseQueryResult = DefinedQueryObserverResult;
+```
+
+Defined in: [preact-query/src/types.ts:168](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L168)
+
+## Type Parameters
+
+### TData
+
+`TData` = `unknown`
+
+### TError
+
+`TError` = `DefaultError`
diff --git a/docs/framework/preact/reference/type-aliases/QueriesOptions.md b/docs/framework/preact/reference/type-aliases/QueriesOptions.md
new file mode 100644
index 0000000000..447796fb90
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/QueriesOptions.md
@@ -0,0 +1,28 @@
+---
+id: QueriesOptions
+title: QueriesOptions
+---
+
+# Type Alias: QueriesOptions\
+
+```ts
+type QueriesOptions = TDepth["length"] extends MAXIMUM_DEPTH ? UseQueryOptionsForUseQueries[] : T extends [] ? [] : T extends [infer Head] ? [...TResults, GetUseQueryOptionsForUseQueries] : T extends [infer Head, ...(infer Tails)] ? QueriesOptions<[...Tails], [...TResults, GetUseQueryOptionsForUseQueries], [...TDepth, 1]> : ReadonlyArray extends T ? T : T extends UseQueryOptionsForUseQueries[] ? UseQueryOptionsForUseQueries[] : UseQueryOptionsForUseQueries[];
+```
+
+Defined in: [preact-query/src/useQueries.ts:147](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/useQueries.ts#L147)
+
+QueriesOptions reducer recursively unwraps function arguments to infer/enforce type param
+
+## Type Parameters
+
+### T
+
+`T` *extends* `any`[]
+
+### TResults
+
+`TResults` *extends* `any`[] = \[\]
+
+### TDepth
+
+`TDepth` *extends* `ReadonlyArray`\<`number`\> = \[\]
diff --git a/docs/framework/preact/reference/type-aliases/QueriesResults.md b/docs/framework/preact/reference/type-aliases/QueriesResults.md
new file mode 100644
index 0000000000..d2299858ed
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/QueriesResults.md
@@ -0,0 +1,28 @@
+---
+id: QueriesResults
+title: QueriesResults
+---
+
+# Type Alias: QueriesResults\
+
+```ts
+type QueriesResults = TDepth["length"] extends MAXIMUM_DEPTH ? UseQueryResult[] : T extends [] ? [] : T extends [infer Head] ? [...TResults, GetUseQueryResult] : T extends [infer Head, ...(infer Tails)] ? QueriesResults<[...Tails], [...TResults, GetUseQueryResult], [...TDepth, 1]> : { [K in keyof T]: GetUseQueryResult };
+```
+
+Defined in: [preact-query/src/useQueries.ts:189](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/useQueries.ts#L189)
+
+QueriesResults reducer recursively maps type param to results
+
+## Type Parameters
+
+### T
+
+`T` *extends* `any`[]
+
+### TResults
+
+`TResults` *extends* `any`[] = \[\]
+
+### TDepth
+
+`TDepth` *extends* `ReadonlyArray`\<`number`\> = \[\]
diff --git a/docs/framework/preact/reference/type-aliases/QueryClientProviderProps.md b/docs/framework/preact/reference/type-aliases/QueryClientProviderProps.md
new file mode 100644
index 0000000000..ad160952f2
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/QueryClientProviderProps.md
@@ -0,0 +1,32 @@
+---
+id: QueryClientProviderProps
+title: QueryClientProviderProps
+---
+
+# Type Alias: QueryClientProviderProps
+
+```ts
+type QueryClientProviderProps = object;
+```
+
+Defined in: [preact-query/src/QueryClientProvider.tsx:23](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/QueryClientProvider.tsx#L23)
+
+## Properties
+
+### children?
+
+```ts
+optional children: ComponentChildren;
+```
+
+Defined in: [preact-query/src/QueryClientProvider.tsx:25](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/QueryClientProvider.tsx#L25)
+
+***
+
+### client
+
+```ts
+client: QueryClient;
+```
+
+Defined in: [preact-query/src/QueryClientProvider.tsx:24](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/QueryClientProvider.tsx#L24)
diff --git a/docs/framework/preact/reference/type-aliases/QueryErrorClearResetFunction.md b/docs/framework/preact/reference/type-aliases/QueryErrorClearResetFunction.md
new file mode 100644
index 0000000000..c918fc2c96
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/QueryErrorClearResetFunction.md
@@ -0,0 +1,16 @@
+---
+id: QueryErrorClearResetFunction
+title: QueryErrorClearResetFunction
+---
+
+# Type Alias: QueryErrorClearResetFunction()
+
+```ts
+type QueryErrorClearResetFunction = () => void;
+```
+
+Defined in: [preact-query/src/QueryErrorResetBoundary.tsx:7](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/QueryErrorResetBoundary.tsx#L7)
+
+## Returns
+
+`void`
diff --git a/docs/framework/preact/reference/type-aliases/QueryErrorIsResetFunction.md b/docs/framework/preact/reference/type-aliases/QueryErrorIsResetFunction.md
new file mode 100644
index 0000000000..ad1eed4726
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/QueryErrorIsResetFunction.md
@@ -0,0 +1,16 @@
+---
+id: QueryErrorIsResetFunction
+title: QueryErrorIsResetFunction
+---
+
+# Type Alias: QueryErrorIsResetFunction()
+
+```ts
+type QueryErrorIsResetFunction = () => boolean;
+```
+
+Defined in: [preact-query/src/QueryErrorResetBoundary.tsx:6](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/QueryErrorResetBoundary.tsx#L6)
+
+## Returns
+
+`boolean`
diff --git a/docs/framework/preact/reference/type-aliases/QueryErrorResetBoundaryFunction.md b/docs/framework/preact/reference/type-aliases/QueryErrorResetBoundaryFunction.md
new file mode 100644
index 0000000000..efd9810c06
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/QueryErrorResetBoundaryFunction.md
@@ -0,0 +1,22 @@
+---
+id: QueryErrorResetBoundaryFunction
+title: QueryErrorResetBoundaryFunction
+---
+
+# Type Alias: QueryErrorResetBoundaryFunction()
+
+```ts
+type QueryErrorResetBoundaryFunction = (value) => ComponentChildren;
+```
+
+Defined in: [preact-query/src/QueryErrorResetBoundary.tsx:39](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/QueryErrorResetBoundary.tsx#L39)
+
+## Parameters
+
+### value
+
+`QueryErrorResetBoundaryValue`
+
+## Returns
+
+`ComponentChildren`
diff --git a/docs/framework/preact/reference/type-aliases/QueryErrorResetFunction.md b/docs/framework/preact/reference/type-aliases/QueryErrorResetFunction.md
new file mode 100644
index 0000000000..f802d41da3
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/QueryErrorResetFunction.md
@@ -0,0 +1,16 @@
+---
+id: QueryErrorResetFunction
+title: QueryErrorResetFunction
+---
+
+# Type Alias: QueryErrorResetFunction()
+
+```ts
+type QueryErrorResetFunction = () => void;
+```
+
+Defined in: [preact-query/src/QueryErrorResetBoundary.tsx:5](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/QueryErrorResetBoundary.tsx#L5)
+
+## Returns
+
+`void`
diff --git a/docs/framework/preact/reference/type-aliases/SuspenseQueriesOptions.md b/docs/framework/preact/reference/type-aliases/SuspenseQueriesOptions.md
new file mode 100644
index 0000000000..3cee23d8d0
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/SuspenseQueriesOptions.md
@@ -0,0 +1,28 @@
+---
+id: SuspenseQueriesOptions
+title: SuspenseQueriesOptions
+---
+
+# Type Alias: SuspenseQueriesOptions\
+
+```ts
+type SuspenseQueriesOptions = TDepth["length"] extends MAXIMUM_DEPTH ? UseSuspenseQueryOptions[] : T extends [] ? [] : T extends [infer Head] ? [...TResults, GetUseSuspenseQueryOptions] : T extends [infer Head, ...(infer Tails)] ? SuspenseQueriesOptions<[...Tails], [...TResults, GetUseSuspenseQueryOptions], [...TDepth, 1]> : unknown[] extends T ? T : T extends UseSuspenseQueryOptions[] ? UseSuspenseQueryOptions[] : UseSuspenseQueryOptions[];
+```
+
+Defined in: [preact-query/src/useSuspenseQueries.ts:109](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/useSuspenseQueries.ts#L109)
+
+SuspenseQueriesOptions reducer recursively unwraps function arguments to infer/enforce type param
+
+## Type Parameters
+
+### T
+
+`T` *extends* `any`[]
+
+### TResults
+
+`TResults` *extends* `any`[] = \[\]
+
+### TDepth
+
+`TDepth` *extends* `ReadonlyArray`\<`number`\> = \[\]
diff --git a/docs/framework/preact/reference/type-aliases/SuspenseQueriesResults.md b/docs/framework/preact/reference/type-aliases/SuspenseQueriesResults.md
new file mode 100644
index 0000000000..601f7e50d5
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/SuspenseQueriesResults.md
@@ -0,0 +1,28 @@
+---
+id: SuspenseQueriesResults
+title: SuspenseQueriesResults
+---
+
+# Type Alias: SuspenseQueriesResults\
+
+```ts
+type SuspenseQueriesResults = TDepth["length"] extends MAXIMUM_DEPTH ? UseSuspenseQueryResult[] : T extends [] ? [] : T extends [infer Head] ? [...TResults, GetUseSuspenseQueryResult] : T extends [infer Head, ...(infer Tails)] ? SuspenseQueriesResults<[...Tails], [...TResults, GetUseSuspenseQueryResult], [...TDepth, 1]> : { [K in keyof T]: GetUseSuspenseQueryResult };
+```
+
+Defined in: [preact-query/src/useSuspenseQueries.ts:146](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/useSuspenseQueries.ts#L146)
+
+SuspenseQueriesResults reducer recursively maps type param to results
+
+## Type Parameters
+
+### T
+
+`T` *extends* `any`[]
+
+### TResults
+
+`TResults` *extends* `any`[] = \[\]
+
+### TDepth
+
+`TDepth` *extends* `ReadonlyArray`\<`number`\> = \[\]
diff --git a/docs/framework/preact/reference/type-aliases/UndefinedInitialDataInfiniteOptions.md b/docs/framework/preact/reference/type-aliases/UndefinedInitialDataInfiniteOptions.md
new file mode 100644
index 0000000000..f220051939
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/UndefinedInitialDataInfiniteOptions.md
@@ -0,0 +1,44 @@
+---
+id: UndefinedInitialDataInfiniteOptions
+title: UndefinedInitialDataInfiniteOptions
+---
+
+# Type Alias: UndefinedInitialDataInfiniteOptions\
+
+```ts
+type UndefinedInitialDataInfiniteOptions = UseInfiniteQueryOptions & object;
+```
+
+Defined in: [preact-query/src/infiniteQueryOptions.ts:13](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/infiniteQueryOptions.ts#L13)
+
+## Type Declaration
+
+### initialData?
+
+```ts
+optional initialData:
+ | NonUndefinedGuard>
+| InitialDataFunction>>;
+```
+
+## Type Parameters
+
+### TQueryFnData
+
+`TQueryFnData`
+
+### TError
+
+`TError` = `DefaultError`
+
+### TData
+
+`TData` = `InfiniteData`\<`TQueryFnData`\>
+
+### TQueryKey
+
+`TQueryKey` *extends* `QueryKey` = `QueryKey`
+
+### TPageParam
+
+`TPageParam` = `unknown`
diff --git a/docs/framework/preact/reference/type-aliases/UndefinedInitialDataOptions.md b/docs/framework/preact/reference/type-aliases/UndefinedInitialDataOptions.md
new file mode 100644
index 0000000000..55d801cb49
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/UndefinedInitialDataOptions.md
@@ -0,0 +1,40 @@
+---
+id: UndefinedInitialDataOptions
+title: UndefinedInitialDataOptions
+---
+
+# Type Alias: UndefinedInitialDataOptions\
+
+```ts
+type UndefinedInitialDataOptions = UseQueryOptions & object;
+```
+
+Defined in: [preact-query/src/queryOptions.ts:13](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/queryOptions.ts#L13)
+
+## Type Declaration
+
+### initialData?
+
+```ts
+optional initialData:
+ | InitialDataFunction>
+| NonUndefinedGuard;
+```
+
+## Type Parameters
+
+### TQueryFnData
+
+`TQueryFnData` = `unknown`
+
+### TError
+
+`TError` = `DefaultError`
+
+### TData
+
+`TData` = `TQueryFnData`
+
+### TQueryKey
+
+`TQueryKey` *extends* `QueryKey` = `QueryKey`
diff --git a/docs/framework/preact/reference/type-aliases/UnusedSkipTokenInfiniteOptions.md b/docs/framework/preact/reference/type-aliases/UnusedSkipTokenInfiniteOptions.md
new file mode 100644
index 0000000000..58cfd65769
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/UnusedSkipTokenInfiniteOptions.md
@@ -0,0 +1,42 @@
+---
+id: UnusedSkipTokenInfiniteOptions
+title: UnusedSkipTokenInfiniteOptions
+---
+
+# Type Alias: UnusedSkipTokenInfiniteOptions\
+
+```ts
+type UnusedSkipTokenInfiniteOptions = OmitKeyof, "queryFn"> & object;
+```
+
+Defined in: [preact-query/src/infiniteQueryOptions.ts:34](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/infiniteQueryOptions.ts#L34)
+
+## Type Declaration
+
+### queryFn?
+
+```ts
+optional queryFn: Exclude["queryFn"], SkipToken | undefined>;
+```
+
+## Type Parameters
+
+### TQueryFnData
+
+`TQueryFnData`
+
+### TError
+
+`TError` = `DefaultError`
+
+### TData
+
+`TData` = `InfiniteData`\<`TQueryFnData`\>
+
+### TQueryKey
+
+`TQueryKey` *extends* `QueryKey` = `QueryKey`
+
+### TPageParam
+
+`TPageParam` = `unknown`
diff --git a/docs/framework/preact/reference/type-aliases/UnusedSkipTokenOptions.md b/docs/framework/preact/reference/type-aliases/UnusedSkipTokenOptions.md
new file mode 100644
index 0000000000..77121dad88
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/UnusedSkipTokenOptions.md
@@ -0,0 +1,38 @@
+---
+id: UnusedSkipTokenOptions
+title: UnusedSkipTokenOptions
+---
+
+# Type Alias: UnusedSkipTokenOptions\
+
+```ts
+type UnusedSkipTokenOptions = OmitKeyof, "queryFn"> & object;
+```
+
+Defined in: [preact-query/src/queryOptions.ts:25](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/queryOptions.ts#L25)
+
+## Type Declaration
+
+### queryFn?
+
+```ts
+optional queryFn: Exclude["queryFn"], SkipToken | undefined>;
+```
+
+## Type Parameters
+
+### TQueryFnData
+
+`TQueryFnData` = `unknown`
+
+### TError
+
+`TError` = `DefaultError`
+
+### TData
+
+`TData` = `TQueryFnData`
+
+### TQueryKey
+
+`TQueryKey` *extends* `QueryKey` = `QueryKey`
diff --git a/docs/framework/preact/reference/type-aliases/UseBaseMutationResult.md b/docs/framework/preact/reference/type-aliases/UseBaseMutationResult.md
new file mode 100644
index 0000000000..7644109bc3
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/UseBaseMutationResult.md
@@ -0,0 +1,40 @@
+---
+id: UseBaseMutationResult
+title: UseBaseMutationResult
+---
+
+# Type Alias: UseBaseMutationResult\
+
+```ts
+type UseBaseMutationResult = Override, {
+ mutate: UseMutateFunction;
+}> & object;
+```
+
+Defined in: [preact-query/src/types.ts:220](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L220)
+
+## Type Declaration
+
+### mutateAsync
+
+```ts
+mutateAsync: UseMutateAsyncFunction;
+```
+
+## Type Parameters
+
+### TData
+
+`TData` = `unknown`
+
+### TError
+
+`TError` = `DefaultError`
+
+### TVariables
+
+`TVariables` = `unknown`
+
+### TOnMutateResult
+
+`TOnMutateResult` = `unknown`
diff --git a/docs/framework/preact/reference/type-aliases/UseBaseQueryResult.md b/docs/framework/preact/reference/type-aliases/UseBaseQueryResult.md
new file mode 100644
index 0000000000..15f97a4ce4
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/UseBaseQueryResult.md
@@ -0,0 +1,22 @@
+---
+id: UseBaseQueryResult
+title: UseBaseQueryResult
+---
+
+# Type Alias: UseBaseQueryResult\
+
+```ts
+type UseBaseQueryResult = QueryObserverResult;
+```
+
+Defined in: [preact-query/src/types.ts:150](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L150)
+
+## Type Parameters
+
+### TData
+
+`TData` = `unknown`
+
+### TError
+
+`TError` = `DefaultError`
diff --git a/docs/framework/preact/reference/type-aliases/UseInfiniteQueryResult.md b/docs/framework/preact/reference/type-aliases/UseInfiniteQueryResult.md
new file mode 100644
index 0000000000..c7edd0e99c
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/UseInfiniteQueryResult.md
@@ -0,0 +1,22 @@
+---
+id: UseInfiniteQueryResult
+title: UseInfiniteQueryResult
+---
+
+# Type Alias: UseInfiniteQueryResult\
+
+```ts
+type UseInfiniteQueryResult = InfiniteQueryObserverResult;
+```
+
+Defined in: [preact-query/src/types.ts:173](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L173)
+
+## Type Parameters
+
+### TData
+
+`TData` = `unknown`
+
+### TError
+
+`TError` = `DefaultError`
diff --git a/docs/framework/preact/reference/type-aliases/UseMutateAsyncFunction.md b/docs/framework/preact/reference/type-aliases/UseMutateAsyncFunction.md
new file mode 100644
index 0000000000..2118f1c631
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/UseMutateAsyncFunction.md
@@ -0,0 +1,30 @@
+---
+id: UseMutateAsyncFunction
+title: UseMutateAsyncFunction
+---
+
+# Type Alias: UseMutateAsyncFunction\
+
+```ts
+type UseMutateAsyncFunction = MutateFunction;
+```
+
+Defined in: [preact-query/src/types.ts:213](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L213)
+
+## Type Parameters
+
+### TData
+
+`TData` = `unknown`
+
+### TError
+
+`TError` = `DefaultError`
+
+### TVariables
+
+`TVariables` = `void`
+
+### TOnMutateResult
+
+`TOnMutateResult` = `unknown`
diff --git a/docs/framework/preact/reference/type-aliases/UseMutateFunction.md b/docs/framework/preact/reference/type-aliases/UseMutateFunction.md
new file mode 100644
index 0000000000..81ce0df628
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/UseMutateFunction.md
@@ -0,0 +1,40 @@
+---
+id: UseMutateFunction
+title: UseMutateFunction
+---
+
+# Type Alias: UseMutateFunction()\
+
+```ts
+type UseMutateFunction = (...args) => void;
+```
+
+Defined in: [preact-query/src/types.ts:202](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L202)
+
+## Type Parameters
+
+### TData
+
+`TData` = `unknown`
+
+### TError
+
+`TError` = `DefaultError`
+
+### TVariables
+
+`TVariables` = `void`
+
+### TOnMutateResult
+
+`TOnMutateResult` = `unknown`
+
+## Parameters
+
+### args
+
+...`Parameters`\<`MutateFunction`\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\>\>
+
+## Returns
+
+`void`
diff --git a/docs/framework/preact/reference/type-aliases/UseMutationResult.md b/docs/framework/preact/reference/type-aliases/UseMutationResult.md
new file mode 100644
index 0000000000..6df3b42e35
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/UseMutationResult.md
@@ -0,0 +1,30 @@
+---
+id: UseMutationResult
+title: UseMutationResult
+---
+
+# Type Alias: UseMutationResult\
+
+```ts
+type UseMutationResult = UseBaseMutationResult;
+```
+
+Defined in: [preact-query/src/types.ts:237](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L237)
+
+## Type Parameters
+
+### TData
+
+`TData` = `unknown`
+
+### TError
+
+`TError` = `DefaultError`
+
+### TVariables
+
+`TVariables` = `unknown`
+
+### TOnMutateResult
+
+`TOnMutateResult` = `unknown`
diff --git a/docs/framework/preact/reference/type-aliases/UseQueryResult.md b/docs/framework/preact/reference/type-aliases/UseQueryResult.md
new file mode 100644
index 0000000000..72e7e932db
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/UseQueryResult.md
@@ -0,0 +1,22 @@
+---
+id: UseQueryResult
+title: UseQueryResult
+---
+
+# Type Alias: UseQueryResult\
+
+```ts
+type UseQueryResult = UseBaseQueryResult;
+```
+
+Defined in: [preact-query/src/types.ts:155](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L155)
+
+## Type Parameters
+
+### TData
+
+`TData` = `unknown`
+
+### TError
+
+`TError` = `DefaultError`
diff --git a/docs/framework/preact/reference/type-aliases/UseSuspenseInfiniteQueryResult.md b/docs/framework/preact/reference/type-aliases/UseSuspenseInfiniteQueryResult.md
new file mode 100644
index 0000000000..206917916e
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/UseSuspenseInfiniteQueryResult.md
@@ -0,0 +1,22 @@
+---
+id: UseSuspenseInfiniteQueryResult
+title: UseSuspenseInfiniteQueryResult
+---
+
+# Type Alias: UseSuspenseInfiniteQueryResult\
+
+```ts
+type UseSuspenseInfiniteQueryResult = OmitKeyof, "isPlaceholderData" | "promise">;
+```
+
+Defined in: [preact-query/src/types.ts:183](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L183)
+
+## Type Parameters
+
+### TData
+
+`TData` = `unknown`
+
+### TError
+
+`TError` = `DefaultError`
diff --git a/docs/framework/preact/reference/type-aliases/UseSuspenseQueryResult.md b/docs/framework/preact/reference/type-aliases/UseSuspenseQueryResult.md
new file mode 100644
index 0000000000..b924c20a99
--- /dev/null
+++ b/docs/framework/preact/reference/type-aliases/UseSuspenseQueryResult.md
@@ -0,0 +1,22 @@
+---
+id: UseSuspenseQueryResult
+title: UseSuspenseQueryResult
+---
+
+# Type Alias: UseSuspenseQueryResult\
+
+```ts
+type UseSuspenseQueryResult = DistributiveOmit, "isPlaceholderData" | "promise">;
+```
+
+Defined in: [preact-query/src/types.ts:160](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/types.ts#L160)
+
+## Type Parameters
+
+### TData
+
+`TData` = `unknown`
+
+### TError
+
+`TError` = `DefaultError`
diff --git a/docs/framework/preact/reference/variables/IsRestoringProvider.md b/docs/framework/preact/reference/variables/IsRestoringProvider.md
new file mode 100644
index 0000000000..9909b1fd45
--- /dev/null
+++ b/docs/framework/preact/reference/variables/IsRestoringProvider.md
@@ -0,0 +1,12 @@
+---
+id: IsRestoringProvider
+title: IsRestoringProvider
+---
+
+# Variable: IsRestoringProvider
+
+```ts
+const IsRestoringProvider: Provider = IsRestoringContext.Provider;
+```
+
+Defined in: [preact-query/src/IsRestoringProvider.ts:7](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/IsRestoringProvider.ts#L7)
diff --git a/docs/framework/preact/reference/variables/QueryClientContext.md b/docs/framework/preact/reference/variables/QueryClientContext.md
new file mode 100644
index 0000000000..c31856e548
--- /dev/null
+++ b/docs/framework/preact/reference/variables/QueryClientContext.md
@@ -0,0 +1,12 @@
+---
+id: QueryClientContext
+title: QueryClientContext
+---
+
+# Variable: QueryClientContext
+
+```ts
+const QueryClientContext: Context;
+```
+
+Defined in: [preact-query/src/QueryClientProvider.tsx:5](https://github.com/theVedanta/query/blob/main/packages/preact-query/src/QueryClientProvider.tsx#L5)
diff --git a/docs/framework/preact/typescript.md b/docs/framework/preact/typescript.md
new file mode 100644
index 0000000000..53058ff1b3
--- /dev/null
+++ b/docs/framework/preact/typescript.md
@@ -0,0 +1,6 @@
+---
+id: typescript
+title: TypeScript
+ref: docs/framework/react/typescript.md
+replace: { 'React': 'Preact', 'react-query': 'preact-query' }
+---
diff --git a/docs/framework/react/guides/request-waterfalls.md b/docs/framework/react/guides/request-waterfalls.md
index aaa350a0e7..e8a3a80014 100644
--- a/docs/framework/react/guides/request-waterfalls.md
+++ b/docs/framework/react/guides/request-waterfalls.md
@@ -250,6 +250,8 @@ Splitting an applications JS-code into smaller chunks and only loading the neces
Consider this a slightly modified version of the Feed example.
+[//]: # 'LazyExample'
+
```tsx
// This lazy loads the GraphFeedItem component, meaning
// it wont start loading until something renders it
@@ -289,6 +291,8 @@ function GraphFeedItem({ feedItem }) {
}
```
+[//]: # 'LazyExample'
+
This example has a double waterfall, looking like this:
```
diff --git a/examples/preact/simple/.gitignore b/examples/preact/simple/.gitignore
new file mode 100644
index 0000000000..a547bf36d8
--- /dev/null
+++ b/examples/preact/simple/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/examples/preact/simple/README.md b/examples/preact/simple/README.md
new file mode 100644
index 0000000000..a9d90bf039
--- /dev/null
+++ b/examples/preact/simple/README.md
@@ -0,0 +1,15 @@
+# `create-preact`
+
+
+
+
+
+Get started using Preact and Vite!
+
+## Getting Started
+
+- `pnpm dev` - Starts a dev server at http://localhost:5173/
+
+- `pnpm build` - Builds for production, emitting to `dist/`
+
+- `pnpm preview` - Starts a server at http://localhost:4173/ to test production build locally
diff --git a/examples/preact/simple/index.html b/examples/preact/simple/index.html
new file mode 100644
index 0000000000..6959245a96
--- /dev/null
+++ b/examples/preact/simple/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+ Vite + Preact
+
+
+
+
+
+
diff --git a/examples/preact/simple/package.json b/examples/preact/simple/package.json
new file mode 100644
index 0000000000..a2fabb1988
--- /dev/null
+++ b/examples/preact/simple/package.json
@@ -0,0 +1,23 @@
+{
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@tanstack/preact-query": "workspace:^",
+ "preact": "^10.28.0"
+ },
+ "devDependencies": {
+ "@preact/preset-vite": "^2.10.2",
+ "eslint": "^9.36.0",
+ "eslint-config-preact": "^2.0.0",
+ "typescript": "^5.9.3",
+ "vite": "^6.4.1"
+ },
+ "eslintConfig": {
+ "extends": "preact"
+ }
+}
diff --git a/examples/preact/simple/public/vite.svg b/examples/preact/simple/public/vite.svg
new file mode 100644
index 0000000000..ffcb6bcf53
--- /dev/null
+++ b/examples/preact/simple/public/vite.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/preact/simple/src/assets/preact.svg b/examples/preact/simple/src/assets/preact.svg
new file mode 100644
index 0000000000..f34e939f68
--- /dev/null
+++ b/examples/preact/simple/src/assets/preact.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/examples/preact/simple/src/index.tsx b/examples/preact/simple/src/index.tsx
new file mode 100644
index 0000000000..89e7a898ed
--- /dev/null
+++ b/examples/preact/simple/src/index.tsx
@@ -0,0 +1,45 @@
+import { render } from 'preact'
+import {
+ QueryClient,
+ QueryClientProvider,
+ useQuery,
+} from '@tanstack/preact-query'
+
+const queryClient = new QueryClient()
+
+export function App() {
+ return (
+
+
+
+ )
+}
+
+const Example = () => {
+ const { isPending, error, data, isFetching } = useQuery({
+ queryKey: ['repoData'],
+ queryFn: async () => {
+ const response = await fetch(
+ 'https://api.github.com/repos/TanStack/query',
+ )
+ return await response.json()
+ },
+ })
+
+ if (isPending) return 'Loading...'
+
+ if (error !== null) return 'An error has occurred: ' + error.message
+
+ return (
+
+
{data.full_name}
+
{data.description}
+
👀 {data.subscribers_count} {' '}
+
✨ {data.stargazers_count} {' '}
+
🍴 {data.forks_count}
+
{isFetching ? 'Updating...' : ''}
+
+ )
+}
+
+render( , document.getElementById('app'))
diff --git a/examples/preact/simple/src/style.css b/examples/preact/simple/src/style.css
new file mode 100644
index 0000000000..7180a9969d
--- /dev/null
+++ b/examples/preact/simple/src/style.css
@@ -0,0 +1,82 @@
+:root {
+ font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
+ line-height: 1.5;
+ font-weight: 400;
+
+ color: #222;
+ background-color: #ffffff;
+
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-text-size-adjust: 100%;
+}
+
+body {
+ margin: 0;
+ display: flex;
+ align-items: center;
+ min-height: 100vh;
+}
+
+#app {
+ max-width: 1280px;
+ margin: 0 auto;
+ text-align: center;
+}
+
+img {
+ margin-bottom: 1.5rem;
+}
+
+img:hover {
+ filter: drop-shadow(0 0 2em #673ab8aa);
+}
+
+section {
+ margin-top: 5rem;
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ column-gap: 1.5rem;
+}
+
+.resource {
+ padding: 0.75rem 1.5rem;
+ border-radius: 0.5rem;
+ text-align: left;
+ text-decoration: none;
+ color: #222;
+ background-color: #f1f1f1;
+ border: 1px solid transparent;
+}
+
+.resource:hover {
+ border: 1px solid #000;
+ box-shadow: 0 25px 50px -12px #673ab888;
+}
+
+@media (max-width: 639px) {
+ #app {
+ margin: 2rem;
+ }
+ section {
+ margin-top: 5rem;
+ grid-template-columns: 1fr;
+ row-gap: 1rem;
+ }
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ color: #ccc;
+ background-color: #1a1a1a;
+ }
+ .resource {
+ color: #ccc;
+ background-color: #161616;
+ }
+ .resource:hover {
+ border: 1px solid #bbb;
+ }
+}
diff --git a/examples/preact/simple/tsconfig.json b/examples/preact/simple/tsconfig.json
new file mode 100644
index 0000000000..77e0f9c539
--- /dev/null
+++ b/examples/preact/simple/tsconfig.json
@@ -0,0 +1,20 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "noEmit": true,
+ "allowJs": true,
+ "checkJs": true,
+
+ /* Preact Config */
+ "jsx": "react-jsx",
+ "jsxImportSource": "preact",
+ "skipLibCheck": true,
+ "paths": {
+ "react": ["./node_modules/preact/compat/"],
+ "react-dom": ["./node_modules/preact/compat/"]
+ }
+ },
+ "include": ["node_modules/vite/client.d.ts", "**/*"]
+}
diff --git a/examples/preact/simple/vite.config.ts b/examples/preact/simple/vite.config.ts
new file mode 100644
index 0000000000..29b326faf0
--- /dev/null
+++ b/examples/preact/simple/vite.config.ts
@@ -0,0 +1,7 @@
+import { defineConfig } from 'vite'
+import preact from '@preact/preset-vite'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [preact()],
+})
diff --git a/package.json b/package.json
index ceedf99f68..03070700fe 100644
--- a/package.json
+++ b/package.json
@@ -82,7 +82,7 @@
"typescript56": "npm:typescript@5.6",
"typescript57": "npm:typescript@5.7",
"vite": "^6.4.1",
- "vitest": "^4.0.17"
+ "vitest": "^4.0.18"
},
"pnpm": {
"overrides": {
diff --git a/packages/preact-query/CHANGELOG.md b/packages/preact-query/CHANGELOG.md
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/preact-query/README.md b/packages/preact-query/README.md
new file mode 100644
index 0000000000..e9deefa858
--- /dev/null
+++ b/packages/preact-query/README.md
@@ -0,0 +1,48 @@
+
+
+
+
+Hooks for fetching, caching and updating asynchronous data in Preact
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Enjoy this library? Try the entire [TanStack](https://tanstack.com)! [TanStack Table](https://github.com/TanStack/table), [TanStack Router](https://github.com/tanstack/router), [TanStack Virtual](https://github.com/tanstack/virtual), [React Charts](https://github.com/TanStack/react-charts), [React Ranger](https://github.com/TanStack/ranger)
+
+## Visit [tanstack.com/query](https://tanstack.com/query) for docs, guides, API and more!
+
+## Quick Features
+
+- Transport/protocol/backend agnostic data fetching (REST, GraphQL, promises, whatever!)
+- Auto Caching + Refetching (stale-while-revalidate, Window Refocus, Polling/Realtime)
+- Parallel + Dependent Queries
+- Mutations + Reactive Query Refetching
+- Multi-layer Cache + Automatic Garbage Collection
+- Paginated + Cursor-based Queries
+- Load-More + Infinite Scroll Queries w/ Scroll Recovery
+- Request Cancellation
+- [Preact Suspense](https://preactjs.com/guide/v10/api-reference/#suspense) + Fetch-As-You-Render Query Prefetching -- Though not recommended because of the bulk preact/compat adds
+- Dedicated Devtools
+
+### [Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
+
+
diff --git a/packages/preact-query/eslint.config.js b/packages/preact-query/eslint.config.js
new file mode 100644
index 0000000000..4e7c7b4c7b
--- /dev/null
+++ b/packages/preact-query/eslint.config.js
@@ -0,0 +1,38 @@
+// @ts-check
+// @ts-ignore: no types for eslint-config-preact
+import preact from 'eslint-config-preact'
+// eslint-config-preact uses typescript-eslint under the hood
+import tseslint from 'typescript-eslint'
+
+import rootConfig from './root.eslint.config.js'
+
+export default [
+ ...rootConfig,
+ ...preact,
+ {
+ files: ['**/*.{ts,tsx}'],
+ languageOptions: {
+ parser: tseslint.parser,
+ parserOptions: {
+ project: true,
+ },
+ },
+ plugins: {
+ 'typescript-eslint': tseslint.plugin,
+ },
+ rules: {
+ // Disable base rule to prevent overload false positives
+ 'no-redeclare': 'off',
+ 'no-duplicate-imports': 'off',
+ 'no-unused-vars': 'off',
+ 'import/order': 'off',
+ 'sort-imports': 'off',
+ 'no-import-assign': 'off',
+ // TS-aware version handles overloads correctly
+ '@typescript-eslint/no-redeclare': 'error',
+ '@typescript-eslint/array-type': 'off',
+ '@typescript-eslint/no-unnecessary-type-assertion': 'off',
+ '@typescript-eslint/no-unnecessary-condition': 'off',
+ },
+ },
+]
diff --git a/packages/preact-query/package.json b/packages/preact-query/package.json
new file mode 100644
index 0000000000..d49fdeee78
--- /dev/null
+++ b/packages/preact-query/package.json
@@ -0,0 +1,84 @@
+{
+ "name": "@tanstack/preact-query",
+ "version": "5.90.11",
+ "description": "Hooks for managing, caching and syncing asynchronous and remote data in preact",
+ "author": "tannerlinsley",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/TanStack/query.git",
+ "directory": "packages/preact-query"
+ },
+ "homepage": "https://tanstack.com/query",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "scripts": {
+ "clean": "premove ./build ./coverage ./dist-ts",
+ "compile": "tsc --build",
+ "test:eslint": "eslint --concurrency=auto ./src",
+ "test:types": "npm-run-all --serial test:types:*",
+ "test:types:ts50": "node ../../node_modules/typescript50/lib/tsc.js --build tsconfig.legacy.json",
+ "test:types:ts51": "node ../../node_modules/typescript51/lib/tsc.js --build tsconfig.legacy.json",
+ "test:types:ts52": "node ../../node_modules/typescript52/lib/tsc.js --build tsconfig.legacy.json",
+ "test:types:ts53": "node ../../node_modules/typescript53/lib/tsc.js --build tsconfig.legacy.json",
+ "test:types:ts54": "node ../../node_modules/typescript54/lib/tsc.js --build tsconfig.legacy.json",
+ "test:types:ts55": "node ../../node_modules/typescript55/lib/tsc.js --build tsconfig.legacy.json",
+ "test:types:ts56": "node ../../node_modules/typescript56/lib/tsc.js --build tsconfig.legacy.json",
+ "test:types:ts57": "node ../../node_modules/typescript57/lib/tsc.js --build tsconfig.legacy.json",
+ "test:types:tscurrent": "tsc --build",
+ "test:lib": "vitest",
+ "test:lib:dev": "pnpm run test:lib --watch",
+ "test:build": "publint --strict && attw --pack",
+ "build": "pnpm build:tsup && pnpm build:codemods",
+ "build:tsup": "tsup --tsconfig tsconfig.prod.json",
+ "build:codemods": "cpy ../query-codemods/* ./build/codemods"
+ },
+ "type": "module",
+ "types": "build/legacy/index.d.ts",
+ "main": "build/legacy/index.cjs",
+ "module": "build/legacy/index.js",
+ "exports": {
+ ".": {
+ "@tanstack/custom-condition": "./src/index.ts",
+ "import": {
+ "types": "./build/modern/index.d.ts",
+ "default": "./build/modern/index.js"
+ },
+ "require": {
+ "types": "./build/modern/index.d.cts",
+ "default": "./build/modern/index.cjs"
+ }
+ },
+ "./package.json": "./package.json"
+ },
+ "sideEffects": false,
+ "files": [
+ "build",
+ "src",
+ "!src/__tests__",
+ "!build/codemods/node_modules",
+ "!build/codemods/vite.config.ts",
+ "!build/codemods/**/__testfixtures__",
+ "!build/codemods/**/__tests__"
+ ],
+ "dependencies": {
+ "@tanstack/query-core": "workspace:*"
+ },
+ "devDependencies": {
+ "@preact/preset-vite": "^2.10.2",
+ "@tanstack/query-persist-client-core": "workspace:*",
+ "@tanstack/query-test-utils": "workspace:*",
+ "@testing-library/preact": "^3.2.4",
+ "cpy-cli": "^5.0.0",
+ "eslint-config-preact": "^2.0.0",
+ "npm-run-all2": "^5.0.0",
+ "preact": "^10.28.0",
+ "preact-render-to-string": "^6.6.4",
+ "typescript-eslint": "^8.54.0"
+ },
+ "peerDependencies": {
+ "preact": "^10.0.0"
+ }
+}
diff --git a/packages/preact-query/root.eslint.config.js b/packages/preact-query/root.eslint.config.js
new file mode 120000
index 0000000000..35dedbe5a4
--- /dev/null
+++ b/packages/preact-query/root.eslint.config.js
@@ -0,0 +1 @@
+../../eslint.config.js
\ No newline at end of file
diff --git a/packages/preact-query/root.tsup.config.js b/packages/preact-query/root.tsup.config.js
new file mode 120000
index 0000000000..fb8e9add9a
--- /dev/null
+++ b/packages/preact-query/root.tsup.config.js
@@ -0,0 +1 @@
+../../scripts/getTsupConfig.js
\ No newline at end of file
diff --git a/packages/preact-query/src/HydrationBoundary.tsx b/packages/preact-query/src/HydrationBoundary.tsx
new file mode 100644
index 0000000000..5437a4a0f5
--- /dev/null
+++ b/packages/preact-query/src/HydrationBoundary.tsx
@@ -0,0 +1,108 @@
+import { hydrate } from '@tanstack/query-core'
+import type {
+ DehydratedState,
+ HydrateOptions,
+ OmitKeyof,
+ QueryClient,
+} from '@tanstack/query-core'
+import type { ComponentChildren } from 'preact'
+import { useEffect, useMemo, useRef } from 'preact/hooks'
+
+import { useQueryClient } from './QueryClientProvider'
+
+export interface HydrationBoundaryProps {
+ state: DehydratedState | null | undefined
+ options?: OmitKeyof & {
+ defaultOptions?: OmitKeyof<
+ Exclude,
+ 'mutations'
+ >
+ }
+ children?: ComponentChildren
+ queryClient?: QueryClient
+}
+
+export const HydrationBoundary = ({
+ children,
+ options = {},
+ state,
+ queryClient,
+}: HydrationBoundaryProps) => {
+ const client = useQueryClient(queryClient)
+
+ const optionsRef = useRef(options)
+ useEffect(() => {
+ optionsRef.current = options
+ })
+
+ // This useMemo is for performance reasons only, everything inside it must
+ // be safe to run in every render and code here should be read as "in render".
+ //
+ // This code needs to happen during the render phase, because after initial
+ // SSR, hydration needs to happen _before_ children render. Also, if hydrating
+ // during a transition, we want to hydrate as much as is safe in render so
+ // we can prerender as much as possible.
+ //
+ // For any queries that already exist in the cache, we want to hold back on
+ // hydrating until _after_ the render phase. The reason for this is that during
+ // transitions, we don't want the existing queries and observers to update to
+ // the new data on the current page, only _after_ the transition is committed.
+ // If the transition is aborted, we will have hydrated any _new_ queries, but
+ // we throw away the fresh data for any existing ones to avoid unexpectedly
+ // updating the UI.
+ const hydrationQueue: DehydratedState['queries'] | undefined = useMemo(() => {
+ if (state) {
+ if (typeof state !== 'object') {
+ return
+ }
+
+ const queryCache = client.getQueryCache()
+ // State is supplied from the outside and we might as well fail
+ // gracefully if it has the wrong shape, so while we type `queries`
+ // as required, we still provide a fallback.
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+ const queries = state.queries || []
+
+ const newQueries: DehydratedState['queries'] = []
+ const existingQueries: DehydratedState['queries'] = []
+ for (const dehydratedQuery of queries) {
+ const existingQuery = queryCache.get(dehydratedQuery.queryHash)
+
+ if (!existingQuery) {
+ newQueries.push(dehydratedQuery)
+ } else {
+ const hydrationIsNewer =
+ dehydratedQuery.state.dataUpdatedAt >
+ existingQuery.state.dataUpdatedAt ||
+ (dehydratedQuery.promise &&
+ existingQuery.state.status !== 'pending' &&
+ existingQuery.state.fetchStatus !== 'fetching' &&
+ dehydratedQuery.dehydratedAt !== undefined &&
+ dehydratedQuery.dehydratedAt > existingQuery.state.dataUpdatedAt)
+
+ if (hydrationIsNewer) {
+ existingQueries.push(dehydratedQuery)
+ }
+ }
+ }
+
+ if (newQueries.length > 0) {
+ // It's actually fine to call this with queries/state that already exists
+ // in the cache, or is older. hydrate() is idempotent for queries.
+ hydrate(client, { queries: newQueries }, optionsRef.current)
+ }
+ if (existingQueries.length > 0) {
+ return existingQueries
+ }
+ }
+ return undefined
+ }, [client, state])
+
+ useEffect(() => {
+ if (hydrationQueue) {
+ hydrate(client, { queries: hydrationQueue }, optionsRef.current)
+ }
+ }, [client, hydrationQueue])
+
+ return children as ComponentChildren
+}
diff --git a/packages/preact-query/src/IsRestoringProvider.ts b/packages/preact-query/src/IsRestoringProvider.ts
new file mode 100644
index 0000000000..a412ada08d
--- /dev/null
+++ b/packages/preact-query/src/IsRestoringProvider.ts
@@ -0,0 +1,7 @@
+import { createContext } from 'preact'
+import { useContext } from 'preact/hooks'
+
+const IsRestoringContext = createContext(false)
+
+export const useIsRestoring = () => useContext(IsRestoringContext)
+export const IsRestoringProvider = IsRestoringContext.Provider
diff --git a/packages/preact-query/src/QueryClientProvider.tsx b/packages/preact-query/src/QueryClientProvider.tsx
new file mode 100644
index 0000000000..b4a1dfa1b5
--- /dev/null
+++ b/packages/preact-query/src/QueryClientProvider.tsx
@@ -0,0 +1,45 @@
+import type { QueryClient } from '@tanstack/query-core'
+import { createContext } from 'preact'
+import type { ComponentChildren, VNode } from 'preact'
+import { useContext, useEffect } from 'preact/hooks'
+
+export const QueryClientContext = createContext(
+ undefined,
+)
+
+export const useQueryClient = (queryClient?: QueryClient) => {
+ const client = useContext(QueryClientContext)
+
+ if (queryClient) {
+ return queryClient
+ }
+
+ if (!client) {
+ throw new Error('No QueryClient set, use QueryClientProvider to set one')
+ }
+
+ return client
+}
+
+export type QueryClientProviderProps = {
+ client: QueryClient
+ children?: ComponentChildren
+}
+
+export const QueryClientProvider = ({
+ client,
+ children,
+}: QueryClientProviderProps): VNode => {
+ useEffect(() => {
+ client.mount()
+ return () => {
+ client.unmount()
+ }
+ }, [client])
+
+ return (
+
+ {children}
+
+ )
+}
diff --git a/packages/preact-query/src/QueryErrorResetBoundary.tsx b/packages/preact-query/src/QueryErrorResetBoundary.tsx
new file mode 100644
index 0000000000..ed038bc31b
--- /dev/null
+++ b/packages/preact-query/src/QueryErrorResetBoundary.tsx
@@ -0,0 +1,57 @@
+import { createContext } from 'preact'
+import type { ComponentChildren } from 'preact'
+import { useContext, useState } from 'preact/hooks'
+
+// CONTEXT
+export type QueryErrorResetFunction = () => void
+export type QueryErrorIsResetFunction = () => boolean
+export type QueryErrorClearResetFunction = () => void
+
+export interface QueryErrorResetBoundaryValue {
+ clearReset: QueryErrorClearResetFunction
+ isReset: QueryErrorIsResetFunction
+ reset: QueryErrorResetFunction
+}
+
+function createValue(): QueryErrorResetBoundaryValue {
+ let isReset = false
+ return {
+ clearReset: () => {
+ isReset = false
+ },
+ reset: () => {
+ isReset = true
+ },
+ isReset: () => {
+ return isReset
+ },
+ }
+}
+
+const QueryErrorResetBoundaryContext = createContext(createValue())
+
+// HOOK
+
+export const useQueryErrorResetBoundary = () =>
+ useContext(QueryErrorResetBoundaryContext)
+
+// COMPONENT
+
+export type QueryErrorResetBoundaryFunction = (
+ value: QueryErrorResetBoundaryValue,
+) => ComponentChildren
+
+export interface QueryErrorResetBoundaryProps {
+ children: QueryErrorResetBoundaryFunction | ComponentChildren
+}
+
+export const QueryErrorResetBoundary = ({
+ children,
+}: QueryErrorResetBoundaryProps) => {
+ const [value] = useState(() => createValue())
+ return (
+
+ {typeof children === 'function' ? children(value) : children}
+
+ )
+}
diff --git a/packages/preact-query/src/__tests__/ErrorBoundary/ErrorBoundary.ts b/packages/preact-query/src/__tests__/ErrorBoundary/ErrorBoundary.ts
new file mode 100644
index 0000000000..c4465e214c
--- /dev/null
+++ b/packages/preact-query/src/__tests__/ErrorBoundary/ErrorBoundary.ts
@@ -0,0 +1,133 @@
+import { createElement, Component } from 'preact'
+import type { ErrorInfo } from 'preact'
+
+import { ErrorBoundaryContext } from './ErrorBoundaryContext'
+import type { ErrorBoundaryProps, FallbackProps } from './types'
+
+type ErrorBoundaryState =
+ | {
+ didCatch: true
+ error: any
+ }
+ | {
+ didCatch: false
+ error: null
+ }
+
+const initialState: ErrorBoundaryState = {
+ didCatch: false,
+ error: null,
+}
+
+export class ErrorBoundary extends Component<
+ ErrorBoundaryProps,
+ ErrorBoundaryState
+> {
+ constructor(props: ErrorBoundaryProps) {
+ super(props)
+
+ this.resetErrorBoundary = this.resetErrorBoundary.bind(this)
+ this.state = initialState
+ }
+
+ static getDerivedStateFromError(error: Error) {
+ return { didCatch: true, error }
+ }
+
+ resetErrorBoundary(...args: any[]) {
+ const { error } = this.state
+
+ if (error !== null) {
+ this.props.onReset?.({
+ args,
+ reason: 'imperative-api',
+ })
+
+ this.setState(initialState)
+ }
+ }
+
+ componentDidCatch(error: Error, info: ErrorInfo) {
+ /**
+ * To emulate the react behaviour of console.error
+ * we add one here to show that the errors bubble up
+ * to the system and can be seen in the console
+ */
+ console.error('%o\n\n%s', error, info)
+ this.props.onError?.(error, info)
+ }
+
+ componentDidUpdate(
+ prevProps: ErrorBoundaryProps,
+ prevState: ErrorBoundaryState,
+ ) {
+ const { didCatch } = this.state
+ const { resetKeys } = this.props
+
+ // There's an edge case where if the thing that triggered the error happens to *also* be in the resetKeys array,
+ // we'd end up resetting the error boundary immediately.
+ // This would likely trigger a second error to be thrown.
+ // So we make sure that we don't check the resetKeys on the first call of cDU after the error is set.
+
+ if (
+ didCatch &&
+ prevState.error !== null &&
+ hasArrayChanged(prevProps.resetKeys, resetKeys)
+ ) {
+ this.props.onReset?.({
+ next: resetKeys,
+ prev: prevProps.resetKeys,
+ reason: 'keys',
+ })
+
+ // eslint-disable-next-line
+ this.setState(initialState)
+ }
+ }
+
+ render() {
+ const { children, fallbackRender, FallbackComponent, fallback } = this.props
+ const { didCatch, error } = this.state
+
+ let childToRender = children
+
+ if (didCatch) {
+ const props: FallbackProps = {
+ error,
+ resetErrorBoundary: this.resetErrorBoundary,
+ }
+
+ if (typeof fallbackRender === 'function') {
+ childToRender = fallbackRender(props)
+ } else if (FallbackComponent) {
+ childToRender = createElement(FallbackComponent, props)
+ } else if (fallback !== undefined) {
+ childToRender = fallback
+ } else {
+ console.error(
+ 'preact-error-boundary requires either a fallback, fallbackRender, or FallbackComponent prop',
+ )
+
+ throw error
+ }
+ }
+
+ return createElement(
+ ErrorBoundaryContext.Provider,
+ {
+ value: {
+ didCatch,
+ error,
+ resetErrorBoundary: this.resetErrorBoundary,
+ },
+ },
+ childToRender,
+ )
+ }
+}
+
+function hasArrayChanged(a: any[] = [], b: any[] = []) {
+ return (
+ a.length !== b.length || a.some((item, index) => !Object.is(item, b[index]))
+ )
+}
diff --git a/packages/preact-query/src/__tests__/ErrorBoundary/ErrorBoundaryContext.ts b/packages/preact-query/src/__tests__/ErrorBoundary/ErrorBoundaryContext.ts
new file mode 100644
index 0000000000..5f0315b848
--- /dev/null
+++ b/packages/preact-query/src/__tests__/ErrorBoundary/ErrorBoundaryContext.ts
@@ -0,0 +1,10 @@
+import { createContext } from 'preact'
+
+type ErrorBoundaryContextType = {
+ didCatch: boolean
+ error: any
+ resetErrorBoundary: (...args: any[]) => void
+}
+
+export const ErrorBoundaryContext =
+ createContext(null)
diff --git a/packages/preact-query/src/__tests__/ErrorBoundary/index.ts b/packages/preact-query/src/__tests__/ErrorBoundary/index.ts
new file mode 100644
index 0000000000..51ee3ea0df
--- /dev/null
+++ b/packages/preact-query/src/__tests__/ErrorBoundary/index.ts
@@ -0,0 +1,9 @@
+/**
+ * Custom Error Boundary port from 'react-error-boundary'
+ * Taken directly from https://github.com/bvaughn/react-error-boundary/
+ * and modified to server a preact use case
+ */
+
+export * from './ErrorBoundary'
+export * from './ErrorBoundaryContext'
+export * from './types'
diff --git a/packages/preact-query/src/__tests__/ErrorBoundary/types.ts b/packages/preact-query/src/__tests__/ErrorBoundary/types.ts
new file mode 100644
index 0000000000..b271682aff
--- /dev/null
+++ b/packages/preact-query/src/__tests__/ErrorBoundary/types.ts
@@ -0,0 +1,48 @@
+import type {
+ ComponentChild,
+ ComponentChildren,
+ ComponentType,
+ ErrorInfo,
+} from 'preact'
+
+export type FallbackProps = {
+ error: any
+ resetErrorBoundary: (...args: any[]) => void
+}
+
+type PropsWithChildren = TProps & {
+ children?: ComponentChildren
+}
+
+type ErrorBoundarySharedProps = PropsWithChildren<{
+ onError?: (error: Error, info: ErrorInfo) => void
+ onReset?: (
+ details:
+ | { reason: 'imperative-api'; args: any[] }
+ | { reason: 'keys'; prev: any[] | undefined; next: any[] | undefined },
+ ) => void
+ resetKeys?: any[]
+}>
+
+export type ErrorBoundaryPropsWithComponent = ErrorBoundarySharedProps & {
+ fallback?: never
+ FallbackComponent: ComponentType
+ fallbackRender?: never
+}
+
+export type ErrorBoundaryPropsWithRender = ErrorBoundarySharedProps & {
+ fallback?: never
+ FallbackComponent?: never
+ fallbackRender: (props: FallbackProps) => ComponentChild
+}
+
+export type ErrorBoundaryPropsWithFallback = ErrorBoundarySharedProps & {
+ fallback: ComponentChild
+ FallbackComponent?: never
+ fallbackRender?: never
+}
+
+export type ErrorBoundaryProps =
+ | ErrorBoundaryPropsWithFallback
+ | ErrorBoundaryPropsWithComponent
+ | ErrorBoundaryPropsWithRender
diff --git a/packages/preact-query/src/__tests__/HydrationBoundary.test.tsx b/packages/preact-query/src/__tests__/HydrationBoundary.test.tsx
new file mode 100644
index 0000000000..a15173ce03
--- /dev/null
+++ b/packages/preact-query/src/__tests__/HydrationBoundary.test.tsx
@@ -0,0 +1,484 @@
+import * as coreModule from '@tanstack/query-core'
+import type { hydrate } from '@tanstack/query-core'
+import { sleep } from '@tanstack/query-test-utils'
+import { render } from '@testing-library/preact'
+import { Suspense, startTransition } from 'preact/compat'
+import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
+
+import {
+ HydrationBoundary,
+ QueryClient,
+ QueryClientProvider,
+ dehydrate,
+ useQuery,
+} from '..'
+
+describe('Preact hydration', () => {
+ let stringifiedState: string
+
+ beforeEach(async () => {
+ vi.useFakeTimers()
+ const queryClient = new QueryClient()
+ queryClient.prefetchQuery({
+ queryKey: ['string'],
+ queryFn: () => sleep(10).then(() => ['stringCached']),
+ })
+ await vi.advanceTimersByTimeAsync(10)
+ const dehydrated = dehydrate(queryClient)
+ stringifiedState = JSON.stringify(dehydrated)
+ queryClient.clear()
+ })
+ afterEach(() => {
+ vi.useRealTimers()
+ })
+
+ test('should hydrate queries to the cache on context', async () => {
+ const dehydratedState = JSON.parse(stringifiedState)
+ const queryClient = new QueryClient()
+
+ function Page() {
+ const { data } = useQuery({
+ queryKey: ['string'],
+ queryFn: () => sleep(20).then(() => ['string']),
+ })
+ return (
+
+
{data}
+
+ )
+ }
+
+ const rendered = render(
+
+
+
+
+ ,
+ )
+
+ expect(rendered.getByText('stringCached')).toBeInTheDocument()
+ await vi.advanceTimersByTimeAsync(21)
+ expect(rendered.getByText('string')).toBeInTheDocument()
+ queryClient.clear()
+ })
+
+ test('should hydrate queries to the cache on custom context', async () => {
+ const queryClientInner = new QueryClient()
+ const queryClientOuter = new QueryClient()
+
+ const dehydratedState = JSON.parse(stringifiedState)
+
+ function Page() {
+ const { data } = useQuery({
+ queryKey: ['string'],
+ queryFn: () => sleep(20).then(() => ['string']),
+ })
+ return (
+
+
{data}
+
+ )
+ }
+
+ const rendered = render(
+
+
+
+
+
+
+ ,
+ )
+
+ expect(rendered.getByText('stringCached')).toBeInTheDocument()
+ await vi.advanceTimersByTimeAsync(21)
+ expect(rendered.getByText('string')).toBeInTheDocument()
+
+ queryClientInner.clear()
+ queryClientOuter.clear()
+ })
+
+ describe('PreactQueryCacheProvider with hydration support', () => {
+ test('should hydrate new queries if queries change', async () => {
+ const dehydratedState = JSON.parse(stringifiedState)
+ const queryClient = new QueryClient()
+
+ function Page({ queryKey }: { queryKey: [string] }) {
+ const { data } = useQuery({
+ queryKey,
+ queryFn: () => sleep(20).then(() => queryKey),
+ })
+ return (
+
+
{data}
+
+ )
+ }
+
+ const rendered = render(
+
+
+
+
+ ,
+ )
+
+ expect(rendered.getByText('stringCached')).toBeInTheDocument()
+ await vi.advanceTimersByTimeAsync(21)
+ expect(rendered.getByText('string')).toBeInTheDocument()
+
+ const intermediateClient = new QueryClient()
+
+ intermediateClient.prefetchQuery({
+ queryKey: ['string'],
+ queryFn: () => sleep(20).then(() => ['should change']),
+ })
+ intermediateClient.prefetchQuery({
+ queryKey: ['added'],
+ queryFn: () => sleep(20).then(() => ['added']),
+ })
+ await vi.advanceTimersByTimeAsync(20)
+ const dehydrated = dehydrate(intermediateClient)
+ intermediateClient.clear()
+
+ rendered.rerender(
+
+
+
+
+
+ ,
+ )
+
+ // Existing observer should not have updated at this point,
+ // as that would indicate a side effect in the render phase
+ expect(rendered.getByText('string')).toBeInTheDocument()
+ // New query data should be available immediately
+ expect(rendered.getByText('added')).toBeInTheDocument()
+
+ await vi.advanceTimersByTimeAsync(0)
+ // After effects phase has had time to run, the observer should have updated
+ expect(rendered.queryByText('string')).not.toBeInTheDocument()
+ expect(rendered.getByText('should change')).toBeInTheDocument()
+
+ queryClient.clear()
+ })
+
+ // When we hydrate in transitions that are later aborted, it could be
+ // confusing to both developers and users if we suddenly updated existing
+ // state on the screen (why did this update when it was not stale, nothing
+ // remounted, I didn't change tabs etc?).
+ // Any queries that does not exist in the cache yet can still be hydrated
+ // since they don't have any observers on the current page that would update.
+ test('should hydrate new but not existing queries if transition is aborted', async () => {
+ const initialDehydratedState = JSON.parse(stringifiedState)
+ const queryClient = new QueryClient()
+
+ function Page({ queryKey }: { queryKey: [string] }) {
+ const { data } = useQuery({
+ queryKey,
+ queryFn: () => sleep(20).then(() => queryKey),
+ })
+ return (
+
+
{data}
+
+ )
+ }
+
+ const rendered = render(
+
+
+
+
+ ,
+ )
+
+ expect(rendered.getByText('stringCached')).toBeInTheDocument()
+ await vi.advanceTimersByTimeAsync(21)
+ expect(rendered.getByText('string')).toBeInTheDocument()
+
+ const intermediateClient = new QueryClient()
+ intermediateClient.prefetchQuery({
+ queryKey: ['string'],
+ queryFn: () => sleep(20).then(() => ['should not change']),
+ })
+ intermediateClient.prefetchQuery({
+ queryKey: ['added'],
+ queryFn: () => sleep(20).then(() => ['added']),
+ })
+ await vi.advanceTimersByTimeAsync(20)
+
+ const newDehydratedState = dehydrate(intermediateClient)
+ intermediateClient.clear()
+
+ function Thrower(): never {
+ throw new Promise(() => {
+ // Never resolve
+ })
+ }
+
+ startTransition(() => {
+ rendered.rerender(
+
+
+
+
+
+
+
+
+ ,
+ )
+
+ expect(rendered.getByText('loading')).toBeInTheDocument()
+ })
+
+ startTransition(() => {
+ rendered.rerender(
+
+
+
+
+
+ ,
+ )
+
+ // This query existed before the transition so it should stay the same
+ expect(rendered.getByText('string')).toBeInTheDocument()
+ expect(
+ rendered.queryByText('should not change'),
+ ).not.toBeInTheDocument()
+ // New query data should be available immediately because it was
+ // hydrated in the previous transition, even though the new dehydrated
+ // state did not contain it
+ expect(rendered.getByText('added')).toBeInTheDocument()
+ })
+
+ await vi.advanceTimersByTimeAsync(20)
+ // It should stay the same even after effects have had a chance to run
+ expect(rendered.getByText('string')).toBeInTheDocument()
+ expect(rendered.queryByText('should not change')).not.toBeInTheDocument()
+
+ queryClient.clear()
+ })
+
+ test('should hydrate queries to new cache if cache changes', async () => {
+ const dehydratedState = JSON.parse(stringifiedState)
+ const queryClient = new QueryClient()
+
+ function Page() {
+ const { data } = useQuery({
+ queryKey: ['string'],
+ queryFn: () => sleep(20).then(() => ['string']),
+ })
+ return (
+
+
{data}
+
+ )
+ }
+
+ const rendered = render(
+
+
+
+
+ ,
+ )
+
+ expect(rendered.getByText('stringCached')).toBeInTheDocument()
+ await vi.advanceTimersByTimeAsync(21)
+ expect(rendered.getByText('string')).toBeInTheDocument()
+ const newClientQueryClient = new QueryClient()
+
+ rendered.rerender(
+
+
+
+
+ ,
+ )
+
+ await vi.advanceTimersByTimeAsync(20)
+ expect(rendered.getByText('string')).toBeInTheDocument()
+
+ queryClient.clear()
+ newClientQueryClient.clear()
+ })
+ })
+
+ test('should not hydrate queries if state is null', async () => {
+ const queryClient = new QueryClient()
+
+ const hydrateSpy = vi.spyOn(coreModule, 'hydrate')
+
+ function Page() {
+ return null
+ }
+
+ render(
+
+
+
+
+ ,
+ )
+
+ await Promise.all(
+ Array.from({ length: 1000 }).map(async (_, index) => {
+ await vi.advanceTimersByTimeAsync(index)
+ expect(hydrateSpy).toHaveBeenCalledTimes(0)
+ }),
+ )
+
+ hydrateSpy.mockRestore()
+ queryClient.clear()
+ })
+
+ test('should not hydrate queries if state is undefined', async () => {
+ const queryClient = new QueryClient()
+
+ const hydrateSpy = vi.spyOn(coreModule, 'hydrate')
+
+ function Page() {
+ return null
+ }
+
+ render(
+
+
+
+
+ ,
+ )
+
+ await vi.advanceTimersByTimeAsync(0)
+ expect(hydrateSpy).toHaveBeenCalledTimes(0)
+
+ hydrateSpy.mockRestore()
+ queryClient.clear()
+ })
+
+ test('should not hydrate queries if state is not an object', async () => {
+ const queryClient = new QueryClient()
+
+ const hydrateSpy = vi.spyOn(coreModule, 'hydrate')
+
+ function Page() {
+ return null
+ }
+
+ render(
+
+
+
+
+ ,
+ )
+
+ await vi.advanceTimersByTimeAsync(0)
+ expect(hydrateSpy).toHaveBeenCalledTimes(0)
+
+ hydrateSpy.mockRestore()
+ queryClient.clear()
+ })
+
+ test('should handle state without queries property gracefully', async () => {
+ const queryClient = new QueryClient()
+
+ const hydrateSpy = vi.spyOn(coreModule, 'hydrate')
+
+ function Page() {
+ return null
+ }
+
+ render(
+
+
+
+
+ ,
+ )
+
+ await vi.advanceTimersByTimeAsync(0)
+ expect(hydrateSpy).toHaveBeenCalledTimes(0)
+
+ hydrateSpy.mockRestore()
+ queryClient.clear()
+ })
+
+ // https://github.com/TanStack/query/issues/8677
+ test('should not infinite loop when hydrating promises that resolve to errors', async () => {
+ const originalHydrate = coreModule.hydrate
+ const hydrateSpy = vi.spyOn(coreModule, 'hydrate')
+ let hydrationCount = 0
+ hydrateSpy.mockImplementation((...args: Parameters) => {
+ hydrationCount++
+ // Arbitrary number
+ if (hydrationCount > 10) {
+ // This is a rough way to detect it. Calling hydrate multiple times with
+ // the same data is usually fine, but in this case it indicates the
+ // logic in HydrationBoundary is not working as expected.
+ throw new Error('Too many hydrations detected')
+ }
+ return originalHydrate(...args)
+ })
+
+ // For the bug to trigger, there needs to already be a query in the cache,
+ // with a dataUpdatedAt earlier than the dehydratedAt of the next query
+ const clientQueryClient = new QueryClient()
+ clientQueryClient.prefetchQuery({
+ queryKey: ['promise'],
+ queryFn: () => sleep(20).then(() => 'existing'),
+ })
+ await vi.advanceTimersByTimeAsync(20)
+
+ const prefetchQueryClient = new QueryClient({
+ defaultOptions: {
+ dehydrate: {
+ shouldDehydrateQuery: () => true,
+ },
+ },
+ })
+ prefetchQueryClient.prefetchQuery({
+ queryKey: ['promise'],
+ queryFn: () =>
+ sleep(10).then(() => Promise.reject(new Error('Query failed'))),
+ })
+
+ const dehydratedState = dehydrate(prefetchQueryClient)
+
+ // Mimic what React/our synchronous thenable does for already rejected promises
+ // @ts-expect-error
+ dehydratedState.queries[0].promise.status = 'failure'
+
+ function Page() {
+ const { data } = useQuery({
+ queryKey: ['promise'],
+ queryFn: () => sleep(20).then(() => ['new']),
+ })
+ return (
+
+
{data}
+
+ )
+ }
+
+ const rendered = render(
+
+
+
+
+ ,
+ )
+
+ expect(rendered.getByText('existing')).toBeInTheDocument()
+ await vi.advanceTimersByTimeAsync(21)
+ expect(rendered.getByText('new')).toBeInTheDocument()
+
+ hydrateSpy.mockRestore()
+ prefetchQueryClient.clear()
+ clientQueryClient.clear()
+ })
+})
diff --git a/packages/preact-query/src/__tests__/QueryClientProvider.test.tsx b/packages/preact-query/src/__tests__/QueryClientProvider.test.tsx
new file mode 100644
index 0000000000..a29a0a409e
--- /dev/null
+++ b/packages/preact-query/src/__tests__/QueryClientProvider.test.tsx
@@ -0,0 +1,166 @@
+import { queryKey, sleep } from '@tanstack/query-test-utils'
+import { render } from '@testing-library/preact'
+import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
+
+import {
+ QueryCache,
+ QueryClient,
+ QueryClientProvider,
+ useQuery,
+ useQueryClient,
+} from '..'
+
+describe('QueryClientProvider', () => {
+ beforeEach(() => {
+ vi.useFakeTimers()
+ })
+
+ afterEach(() => {
+ vi.useRealTimers()
+ })
+
+ test('sets a specific cache for all queries to use', async () => {
+ const key = queryKey()
+
+ const queryCache = new QueryCache()
+ const queryClient = new QueryClient({ queryCache })
+
+ function Page() {
+ const { data } = useQuery({
+ queryKey: key,
+ queryFn: () => sleep(10).then(() => 'test'),
+ })
+
+ return (
+
+
{data}
+
+ )
+ }
+
+ const rendered = render(
+
+
+ ,
+ )
+
+ await vi.advanceTimersByTimeAsync(11)
+ expect(rendered.getByText('test')).toBeInTheDocument()
+
+ expect(queryCache.find({ queryKey: key })).toBeDefined()
+ })
+
+ test('allows multiple caches to be partitioned', async () => {
+ const key1 = queryKey()
+ const key2 = queryKey()
+
+ const queryCache1 = new QueryCache()
+ const queryCache2 = new QueryCache()
+
+ const queryClient1 = new QueryClient({ queryCache: queryCache1 })
+ const queryClient2 = new QueryClient({ queryCache: queryCache2 })
+
+ function Page1() {
+ const { data } = useQuery({
+ queryKey: key1,
+ queryFn: () => sleep(10).then(() => 'test1'),
+ })
+
+ return (
+
+
{data}
+
+ )
+ }
+ function Page2() {
+ const { data } = useQuery({
+ queryKey: key2,
+ queryFn: () => sleep(10).then(() => 'test2'),
+ })
+
+ return (
+
+
{data}
+
+ )
+ }
+
+ const rendered = render(
+ <>
+
+
+
+
+
+
+ >,
+ )
+
+ await vi.advanceTimersByTimeAsync(11)
+ expect(rendered.getByText('test1')).toBeInTheDocument()
+ expect(rendered.getByText('test2')).toBeInTheDocument()
+
+ expect(queryCache1.find({ queryKey: key1 })).toBeDefined()
+ expect(queryCache1.find({ queryKey: key2 })).not.toBeDefined()
+ expect(queryCache2.find({ queryKey: key1 })).not.toBeDefined()
+ expect(queryCache2.find({ queryKey: key2 })).toBeDefined()
+ })
+
+ test("uses defaultOptions for queries when they don't provide their own config", async () => {
+ const key = queryKey()
+
+ const queryCache = new QueryCache()
+ const queryClient = new QueryClient({
+ queryCache,
+ defaultOptions: {
+ queries: {
+ gcTime: Infinity,
+ },
+ },
+ })
+
+ function Page() {
+ const { data } = useQuery({
+ queryKey: key,
+ queryFn: () => sleep(10).then(() => 'test'),
+ })
+
+ return (
+
+
{data}
+
+ )
+ }
+
+ const rendered = render(
+
+
+ ,
+ )
+
+ await vi.advanceTimersByTimeAsync(11)
+ expect(rendered.getByText('test')).toBeInTheDocument()
+
+ expect(queryCache.find({ queryKey: key })).toBeDefined()
+ expect(queryCache.find({ queryKey: key })?.options.gcTime).toBe(Infinity)
+ })
+
+ describe('useQueryClient', () => {
+ test('should throw an error if no query client has been set', () => {
+ const consoleMock = vi
+ .spyOn(console, 'error')
+ .mockImplementation(() => undefined)
+
+ function Page() {
+ useQueryClient()
+ return null
+ }
+
+ expect(() => render( )).toThrow(
+ 'No QueryClient set, use QueryClientProvider to set one',
+ )
+
+ consoleMock.mockRestore()
+ })
+ })
+})
diff --git a/packages/preact-query/src/__tests__/QueryResetErrorBoundary.test.tsx b/packages/preact-query/src/__tests__/QueryResetErrorBoundary.test.tsx
new file mode 100644
index 0000000000..1f1bb00b8d
--- /dev/null
+++ b/packages/preact-query/src/__tests__/QueryResetErrorBoundary.test.tsx
@@ -0,0 +1,868 @@
+import { queryKey, sleep } from '@tanstack/query-test-utils'
+import { fireEvent } from '@testing-library/preact'
+import { Suspense } from 'preact/compat'
+import { useEffect, useState } from 'preact/hooks'
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
+
+import {
+ QueryCache,
+ QueryClient,
+ QueryErrorResetBoundary,
+ useQueries,
+ useQuery,
+ useSuspenseQueries,
+ useSuspenseQuery,
+} from '..'
+import { ErrorBoundary } from './ErrorBoundary'
+import { renderWithClient } from './utils'
+
+describe('QueryErrorResetBoundary', () => {
+ beforeEach(() => {
+ vi.useFakeTimers()
+ })
+
+ afterEach(() => {
+ vi.useRealTimers()
+ })
+
+ const queryCache = new QueryCache()
+ const queryClient = new QueryClient({ queryCache })
+
+ describe('useQuery', () => {
+ it('should retry fetch if the reset error boundary has been reset', async () => {
+ const consoleMock = vi
+ .spyOn(console, 'error')
+ .mockImplementation(() => undefined)
+ const key = queryKey()
+
+ let succeed = false
+
+ function Page() {
+ const { data } = useQuery({
+ queryKey: key,
+ queryFn: () =>
+ sleep(10).then(() => {
+ if (!succeed) throw new Error('Error')
+ return 'data'
+ }),
+ retry: false,
+ throwOnError: true,
+ })
+
+ return {data}
+ }
+
+ const rendered = renderWithClient(
+ queryClient,
+
+ {({ reset }) => (
+ (
+
+
error boundary
+
{
+ resetErrorBoundary()
+ }}
+ >
+ retry
+
+
+ )}
+ >
+
+
+ )}
+ ,
+ )
+
+ await vi.advanceTimersByTimeAsync(11)
+ expect(rendered.getByText('error boundary')).toBeInTheDocument()
+ expect(rendered.getByText('retry')).toBeInTheDocument()
+
+ succeed = true
+
+ fireEvent.click(rendered.getByText('retry'))
+ await vi.advanceTimersByTimeAsync(11)
+ expect(rendered.getByText('data')).toBeInTheDocument()
+
+ consoleMock.mockRestore()
+ })
+
+ it('should not throw error if query is disabled', async () => {
+ const consoleMock = vi
+ .spyOn(console, 'error')
+ .mockImplementation(() => undefined)
+ const key = queryKey()
+
+ let succeed = false
+
+ function Page() {
+ const { data, status } = useQuery({
+ queryKey: key,
+ queryFn: () =>
+ sleep(10).then(() => {
+ if (!succeed) throw new Error('Error')
+ return 'data'
+ }),
+ retry: false,
+ enabled: !succeed,
+ throwOnError: true,
+ })
+
+ return (
+
+
status: {status}
+
{data}
+
+ )
+ }
+
+ const rendered = renderWithClient(
+ queryClient,
+
+ {({ reset }) => (
+ (
+
+
error boundary
+
{
+ resetErrorBoundary()
+ }}
+ >
+ retry
+
+
+ )}
+ >
+
+
+ )}
+ ,
+ )
+
+ await vi.advanceTimersByTimeAsync(11)
+ expect(rendered.getByText('error boundary')).toBeInTheDocument()
+ expect(rendered.getByText('retry')).toBeInTheDocument()
+
+ succeed = true
+
+ fireEvent.click(rendered.getByText('retry'))
+ await vi.advanceTimersByTimeAsync(11)
+ expect(rendered.getByText('status: error')).toBeInTheDocument()
+
+ consoleMock.mockRestore()
+ })
+
+ it('should not throw error if query is disabled, and refetch if query becomes enabled again', async () => {
+ const consoleMock = vi
+ .spyOn(console, 'error')
+ .mockImplementation(() => undefined)
+
+ const key = queryKey()
+
+ let succeed = false
+
+ function Page() {
+ const [enabled, setEnabled] = useState(false)
+ const { data } = useQuery({
+ queryKey: key,
+ queryFn: () =>
+ sleep(10).then(() => {
+ if (!succeed) throw new Error('Error')
+ return 'data'
+ }),
+ retry: false,
+ enabled,
+ throwOnError: true,
+ })
+
+ useEffect(() => {
+ setEnabled(true)
+ }, [])
+
+ return {data}
+ }
+
+ const rendered = renderWithClient(
+ queryClient,
+
+ {({ reset }) => (
+ (
+
+
error boundary
+
{
+ resetErrorBoundary()
+ }}
+ >
+ retry
+
+
+ )}
+ >
+
+
+ )}
+ ,
+ )
+
+ await vi.advanceTimersByTimeAsync(11)
+ expect(rendered.getByText('error boundary')).toBeInTheDocument()
+ expect(rendered.getByText('retry')).toBeInTheDocument()
+
+ succeed = true
+
+ fireEvent.click(rendered.getByText('retry'))
+ await vi.advanceTimersByTimeAsync(11)
+ expect(rendered.getByText('data')).toBeInTheDocument()
+
+ consoleMock.mockRestore()
+ })
+
+ it('should throw error if query is disabled and manually refetch', async () => {
+ const consoleMock = vi
+ .spyOn(console, 'error')
+ .mockImplementation(() => undefined)
+
+ const key = queryKey()
+
+ function Page() {
+ const { data, refetch, status, fetchStatus } = useQuery({
+ queryKey: key,
+ queryFn: () =>
+ sleep(10).then(() => Promise.reject(new Error('Error'))),
+ retry: false,
+ enabled: false,
+ throwOnError: true,
+ })
+
+ return (
+
+
refetch()}>refetch
+
+ status: {status}, fetchStatus: {fetchStatus}
+
+
{data}
+
+ )
+ }
+
+ const rendered = renderWithClient(
+ queryClient,
+
+ {({ reset }) => (
+ (
+
+
error boundary
+
{
+ resetErrorBoundary()
+ }}
+ >
+ retry
+
+
+ )}
+ >
+
+
+ )}
+ ,
+ )
+
+ expect(
+ rendered.getByText('status: pending, fetchStatus: idle'),
+ ).toBeInTheDocument()
+ await vi.advanceTimersByTimeAsync(11)
+ expect(
+ rendered.getByText('status: pending, fetchStatus: idle'),
+ ).toBeInTheDocument()
+
+ fireEvent.click(rendered.getByRole('button', { name: /refetch/i }))
+ await vi.advanceTimersByTimeAsync(11)
+ expect(rendered.getByText('error boundary')).toBeInTheDocument()
+
+ consoleMock.mockRestore()
+ })
+
+ it('should not retry fetch if the reset error boundary has not been reset', async () => {
+ const consoleMock = vi
+ .spyOn(console, 'error')
+ .mockImplementation(() => undefined)
+
+ const key = queryKey()
+
+ let succeed = false
+
+ function Page() {
+ const { data } = useQuery({
+ queryKey: key,
+ queryFn: () =>
+ sleep(10).then(() => {
+ if (!succeed) throw new Error('Error')
+ return 'data'
+ }),
+ retry: false,
+ throwOnError: true,
+ })
+
+ return {data}
+ }
+
+ const rendered = renderWithClient(
+ queryClient,
+
+ {() => (
+ (
+
+
error boundary
+
{
+ resetErrorBoundary()
+ }}
+ >
+ retry
+
+
+ )}
+ >
+
+
+ )}
+ ,
+ )
+
+ await vi.advanceTimersByTimeAsync(11)
+ expect(rendered.getByText('error boundary')).toBeInTheDocument()
+ expect(rendered.getByText('retry')).toBeInTheDocument()
+
+ succeed = true
+
+ fireEvent.click(rendered.getByText('retry'))
+ await vi.advanceTimersByTimeAsync(11)
+ expect(rendered.getByText('error boundary')).toBeInTheDocument()
+
+ consoleMock.mockRestore()
+ })
+
+ it('should retry fetch if the reset error boundary has been reset and the query contains data from a previous fetch', async () => {
+ const consoleMock = vi
+ .spyOn(console, 'error')
+ .mockImplementation(() => undefined)
+
+ const key = queryKey()
+
+ let succeed = false
+
+ function Page() {
+ const { data } = useQuery({
+ queryKey: key,
+ queryFn: () =>
+ sleep(10).then(() => {
+ if (!succeed) throw new Error('Error')
+ return 'data'
+ }),
+ retry: false,
+ throwOnError: true,
+ initialData: 'initial',
+ })
+
+ return {data}
+ }
+
+ const rendered = renderWithClient(
+ queryClient,
+
+ {({ reset }) => (
+ (
+
+
error boundary
+
{
+ resetErrorBoundary()
+ }}
+ >
+ retry
+
+
+ )}
+ >
+
+
+ )}
+ ,
+ )
+
+ expect(rendered.getByText('initial')).toBeInTheDocument()
+ await vi.advanceTimersByTimeAsync(11)
+ expect(rendered.getByText('error boundary')).toBeInTheDocument()
+ expect(rendered.getByText('retry')).toBeInTheDocument()
+
+ succeed = true
+
+ fireEvent.click(rendered.getByText('retry'))
+ await vi.advanceTimersByTimeAsync(11)
+ expect(rendered.getByText('data')).toBeInTheDocument()
+
+ consoleMock.mockRestore()
+ })
+
+ it('should not retry fetch if the reset error boundary has not been reset after a previous reset', async () => {
+ const consoleMock = vi
+ .spyOn(console, 'error')
+ .mockImplementation(() => undefined)
+
+ const key = queryKey()
+
+ let succeed = false
+ let shouldReset = false
+
+ function Page() {
+ const { data } = useQuery({
+ queryKey: key,
+ queryFn: () =>
+ sleep(10).then(() => {
+ if (!succeed) throw new Error('Error')
+ return 'data'
+ }),
+ retry: false,
+ throwOnError: true,
+ })
+
+ return {data}
+ }
+
+ const rendered = renderWithClient(
+ queryClient,
+
+ {({ reset }) => (
+ {
+ if (shouldReset) {
+ reset()
+ }
+ }}
+ fallbackRender={({ resetErrorBoundary }) => (
+
+
error boundary
+
{
+ resetErrorBoundary()
+ }}
+ >
+ retry
+
+
+ )}
+ >
+
+
+ )}
+ ,
+ )
+
+ await vi.advanceTimersByTimeAsync(11)
+ expect(rendered.getByText('error boundary')).toBeInTheDocument()
+ expect(rendered.getByText('retry')).toBeInTheDocument()
+
+ succeed = false
+ shouldReset = true
+
+ await vi.advanceTimersByTimeAsync(11)
+ expect(rendered.getByText('error boundary')).toBeInTheDocument()
+ expect(rendered.getByText('retry')).toBeInTheDocument()
+
+ succeed = true
+ shouldReset = false
+
+ fireEvent.click(rendered.getByText('retry'))
+ await vi.advanceTimersByTimeAsync(11)
+ expect(rendered.getByText('error boundary')).toBeInTheDocument()
+
+ succeed = true
+ shouldReset = true
+
+ fireEvent.click(rendered.getByText('retry'))
+ await vi.advanceTimersByTimeAsync(11)
+ expect(rendered.getByText('data')).toBeInTheDocument()
+
+ consoleMock.mockRestore()
+ })
+
+ it('should throw again on error after the reset error boundary has been reset', async () => {
+ const consoleMock = vi
+ .spyOn(console, 'error')
+ .mockImplementation(() => undefined)
+
+ const key = queryKey()
+ let fetchCount = 0
+
+ function Page() {
+ const { data } = useQuery({
+ queryKey: key,
+ queryFn: () =>
+ sleep(10).then(() => {
+ fetchCount++
+ throw new Error('Error')
+ }),
+ retry: false,
+ throwOnError: true,
+ })
+
+ return {data}
+ }
+
+ const rendered = renderWithClient(
+ queryClient,
+
+ {({ reset }) => (
+ (
+
+
error boundary
+
{
+ resetErrorBoundary()
+ }}
+ >
+ retry
+
+
+ )}
+ >
+
+
+ )}
+ ,
+ )
+
+ await vi.advanceTimersByTimeAsync(11)
+ expect(rendered.getByText('error boundary')).toBeInTheDocument()
+ expect(rendered.getByText('retry')).toBeInTheDocument()
+
+ fireEvent.click(rendered.getByText('retry'))
+ await vi.advanceTimersByTimeAsync(11)
+ expect(rendered.getByText('error boundary')).toBeInTheDocument()
+ expect(rendered.getByText('retry')).toBeInTheDocument()
+
+ fireEvent.click(rendered.getByText('retry'))
+ await vi.advanceTimersByTimeAsync(11)
+ expect(rendered.getByText('error boundary')).toBeInTheDocument()
+
+ expect(fetchCount).toBe(3)
+
+ consoleMock.mockRestore()
+ })
+
+ it('should never render the component while the query is in error state', async () => {
+ const consoleMock = vi
+ .spyOn(console, 'error')
+ .mockImplementation(() => undefined)
+
+ const key = queryKey()
+ let fetchCount = 0
+ let renders = 0
+
+ function Page() {
+ const { data } = useSuspenseQuery({
+ queryKey: key,
+ queryFn: () =>
+ sleep(10).then(() => {
+ fetchCount++
+ if (fetchCount > 2) return 'data'
+ throw new Error('Error')
+ }),
+ retry: false,
+ })
+
+ renders++
+
+ return {data}
+ }
+
+ const rendered = renderWithClient(
+ queryClient,
+
+ {({ reset }) => (
+ (
+
+
error boundary
+
{
+ resetErrorBoundary()
+ }}
+ >
+ retry
+
+
+ )}
+ >
+ loading}>
+
+
+
+ )}
+ ,
+ )
+
+ expect(rendered.getByText('loading')).toBeInTheDocument()
+ await vi.advanceTimersByTimeAsync(10)
+ expect(rendered.getByText('error boundary')).toBeInTheDocument()
+ expect(rendered.getByText('retry')).toBeInTheDocument()
+
+ fireEvent.click(rendered.getByText('retry'))
+ expect(rendered.getByText('loading')).toBeInTheDocument()
+ await vi.advanceTimersByTimeAsync(10)
+ expect(rendered.getByText('error boundary')).toBeInTheDocument()
+ expect(rendered.getByText('retry')).toBeInTheDocument()
+
+ fireEvent.click(rendered.getByText('retry'))
+ expect(rendered.getByText('loading')).toBeInTheDocument()
+ await vi.advanceTimersByTimeAsync(10)
+ expect(rendered.getByText('data')).toBeInTheDocument()
+
+ expect(fetchCount).toBe(3)
+ expect(renders).toBe(1)
+
+ consoleMock.mockRestore()
+ })
+
+ it('should render children', () => {
+ const consoleMock = vi
+ .spyOn(console, 'error')
+ .mockImplementation(() => undefined)
+
+ function Page() {
+ return (
+
+ page
+
+ )
+ }
+
+ const rendered = renderWithClient(
+ queryClient,
+
+
+ ,
+ )
+
+ expect(rendered.queryByText('page')).not.toBeNull()
+
+ consoleMock.mockRestore()
+ })
+
+ it('should show error boundary when using tracked queries even though we do not track the error field', async () => {
+ const consoleMock = vi
+ .spyOn(console, 'error')
+ .mockImplementation(() => undefined)
+
+ const key = queryKey()
+
+ let succeed = false
+
+ function Page() {
+ const { data } = useQuery({
+ queryKey: key,
+ queryFn: () =>
+ sleep(10).then(() => {
+ if (!succeed) throw new Error('Error')
+ return 'data'
+ }),
+ retry: false,
+ throwOnError: true,
+ })
+
+ return {data}
+ }
+
+ const rendered = renderWithClient(
+ queryClient,
+
+ {({ reset }) => (
+ (
+
+
error boundary
+
{
+ resetErrorBoundary()
+ }}
+ >
+ retry
+
+
+ )}
+ >
+
+
+ )}
+ ,
+ )
+
+ await vi.advanceTimersByTimeAsync(11)
+ expect(rendered.getByText('error boundary')).toBeInTheDocument()
+ expect(rendered.getByText('retry')).toBeInTheDocument()
+
+ succeed = true
+
+ fireEvent.click(rendered.getByText('retry'))
+ await vi.advanceTimersByTimeAsync(11)
+ expect(rendered.getByText('data')).toBeInTheDocument()
+
+ consoleMock.mockRestore()
+ })
+ })
+
+ describe('useQueries', () => {
+ it('should retry fetch if the reset error boundary has been reset', async () => {
+ const consoleMock = vi
+ .spyOn(console, 'error')
+ .mockImplementation(() => undefined)
+ const key = queryKey()
+
+ let succeed = false
+
+ function Page() {
+ const [{ data }] = useQueries({
+ queries: [
+ {
+ queryKey: key,
+ queryFn: () =>
+ sleep(10).then(() => {
+ if (!succeed) throw new Error('Error')
+ return 'data'
+ }),
+ retry: false,
+ throwOnError: true,
+ retryOnMount: true,
+ },
+ ],
+ })
+
+ return {data}
+ }
+
+ const rendered = renderWithClient(
+ queryClient,
+
+ {({ reset }) => (
+ (
+
+
error boundary
+
{
+ resetErrorBoundary()
+ }}
+ >
+ retry
+
+
+ )}
+ >
+
+
+ )}
+ ,
+ )
+
+ await vi.advanceTimersByTimeAsync(11)
+ expect(rendered.getByText('error boundary')).toBeInTheDocument()
+ expect(rendered.getByText('retry')).toBeInTheDocument()
+
+ succeed = true
+
+ fireEvent.click(rendered.getByText('retry'))
+ await vi.advanceTimersByTimeAsync(11)
+ expect(rendered.getByText('data')).toBeInTheDocument()
+
+ consoleMock.mockRestore()
+ })
+
+ it('with suspense should retry fetch if the reset error boundary has been reset', async () => {
+ const key = queryKey()
+ const consoleMock = vi
+ .spyOn(console, 'error')
+ .mockImplementation(() => undefined)
+
+ let succeed = false
+
+ function Page() {
+ const [{ data }] = useSuspenseQueries({
+ queries: [
+ {
+ queryKey: key,
+ queryFn: () =>
+ sleep(10).then(() => {
+ if (!succeed) throw new Error('Error')
+ return 'data'
+ }),
+ retry: false,
+ retryOnMount: true,
+ },
+ ],
+ })
+
+ return {data}
+ }
+
+ const rendered = renderWithClient(
+ queryClient,
+
+ {({ reset }) => (
+ (
+
+
error boundary
+
{
+ resetErrorBoundary()
+ }}
+ >
+ retry
+
+
+ )}
+ >
+
+
+
+
+ )}
+ ,
+ )
+
+ expect(rendered.getByText('loading')).toBeInTheDocument()
+ await vi.advanceTimersByTimeAsync(10)
+ expect(rendered.getByText('error boundary')).toBeInTheDocument()
+ expect(rendered.getByText('retry')).toBeInTheDocument()
+
+ succeed = true
+
+ fireEvent.click(rendered.getByText('retry'))
+ expect(rendered.getByText('loading')).toBeInTheDocument()
+ await vi.advanceTimersByTimeAsync(10)
+ expect(rendered.getByText('data')).toBeInTheDocument()
+
+ consoleMock.mockRestore()
+ })
+ })
+})
diff --git a/packages/preact-query/src/__tests__/fine-grained-persister.test.tsx b/packages/preact-query/src/__tests__/fine-grained-persister.test.tsx
new file mode 100644
index 0000000000..87bd366253
--- /dev/null
+++ b/packages/preact-query/src/__tests__/fine-grained-persister.test.tsx
@@ -0,0 +1,180 @@
+import {
+ PERSISTER_KEY_PREFIX,
+ experimental_createQueryPersister,
+} from '@tanstack/query-persist-client-core'
+import { queryKey, sleep } from '@tanstack/query-test-utils'
+import { useState } from 'preact/hooks'
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
+
+import { QueryCache, QueryClient, hashKey, useQuery } from '..'
+import { renderWithClient } from './utils'
+
+describe('fine grained persister', () => {
+ beforeEach(() => {
+ vi.useFakeTimers()
+ })
+
+ afterEach(() => {
+ vi.useRealTimers()
+ })
+
+ const queryCache = new QueryCache()
+ const queryClient = new QueryClient({ queryCache })
+
+ it('should restore query state from persister and not refetch', async () => {
+ const key = queryKey()
+ const hash = hashKey(key)
+ const spy = vi.fn(() => Promise.resolve('Works from queryFn'))
+
+ const mapStorage = new Map()
+ const storage = {
+ getItem: (itemKey: string) => Promise.resolve(mapStorage.get(itemKey)),
+ setItem: (itemKey: string, value: unknown) => {
+ mapStorage.set(itemKey, value)
+ return Promise.resolve()
+ },
+ removeItem: (itemKey: string) => {
+ mapStorage.delete(itemKey)
+ return Promise.resolve()
+ },
+ }
+
+ await storage.setItem(
+ `${PERSISTER_KEY_PREFIX}-${hash}`,
+ JSON.stringify({
+ buster: '',
+ queryHash: hash,
+ queryKey: key,
+ state: {
+ dataUpdatedAt: Date.now(),
+ data: 'Works from persister',
+ },
+ }),
+ )
+
+ function Test() {
+ const [_, setRef] = useState()
+
+ const { data } = useQuery({
+ queryKey: key,
+ queryFn: spy,
+ persister: experimental_createQueryPersister({
+ storage,
+ }).persisterFn,
+ staleTime: 5000,
+ })
+
+ return setRef(value)}>{data}
+ }
+
+ const rendered = renderWithClient(queryClient, )
+
+ await vi.advanceTimersByTimeAsync(0)
+ expect(rendered.getByText('Works from persister')).toBeInTheDocument()
+ expect(spy).not.toHaveBeenCalled()
+ })
+
+ it('should restore query state from persister and refetch', async () => {
+ const key = queryKey()
+ const hash = hashKey(key)
+ const spy = vi.fn(async () => {
+ await sleep(5)
+
+ return 'Works from queryFn'
+ })
+
+ const mapStorage = new Map()
+ const storage = {
+ getItem: (itemKey: string) => Promise.resolve(mapStorage.get(itemKey)),
+ setItem: (itemKey: string, value: unknown) => {
+ mapStorage.set(itemKey, value)
+ return Promise.resolve()
+ },
+ removeItem: (itemKey: string) => {
+ mapStorage.delete(itemKey)
+ return Promise.resolve()
+ },
+ }
+
+ await storage.setItem(
+ `${PERSISTER_KEY_PREFIX}-${hash}`,
+ JSON.stringify({
+ buster: '',
+ queryHash: hash,
+ queryKey: key,
+ state: {
+ dataUpdatedAt: Date.now(),
+ data: 'Works from persister',
+ },
+ }),
+ )
+
+ function Test() {
+ const [_, setRef] = useState()
+
+ const { data } = useQuery({
+ queryKey: key,
+ queryFn: spy,
+ persister: experimental_createQueryPersister({
+ storage,
+ }).persisterFn,
+ })
+
+ return setRef(value)}>{data}
+ }
+
+ const rendered = renderWithClient(queryClient, )
+
+ await vi.advanceTimersByTimeAsync(0)
+ expect(rendered.getByText('Works from persister')).toBeInTheDocument()
+ await vi.advanceTimersByTimeAsync(6)
+ expect(rendered.getByText('Works from queryFn')).toBeInTheDocument()
+ expect(spy).toHaveBeenCalledTimes(1)
+ })
+
+ it('should store query state to persister after fetch', async () => {
+ const key = queryKey()
+ const hash = hashKey(key)
+ const spy = vi.fn(() => Promise.resolve('Works from queryFn'))
+
+ const mapStorage = new Map()
+ const storage = {
+ getItem: (itemKey: string) => Promise.resolve(mapStorage.get(itemKey)),
+ setItem: (itemKey: string, value: unknown) => {
+ mapStorage.set(itemKey, value)
+ return Promise.resolve()
+ },
+ removeItem: (itemKey: string) => {
+ mapStorage.delete(itemKey)
+ return Promise.resolve()
+ },
+ }
+
+ function Test() {
+ const [_, setRef] = useState()
+
+ const { data } = useQuery({
+ queryKey: key,
+ queryFn: spy,
+ persister: experimental_createQueryPersister({
+ storage,
+ }).persisterFn,
+ })
+
+ return setRef(value)}>{data}
+ }
+
+ const rendered = renderWithClient(queryClient, )
+
+ await vi.advanceTimersByTimeAsync(0)
+ expect(rendered.getByText('Works from queryFn')).toBeInTheDocument()
+ expect(spy).toHaveBeenCalledTimes(1)
+
+ const storedItem = await storage.getItem(`${PERSISTER_KEY_PREFIX}-${hash}`)
+ expect(JSON.parse(storedItem)).toMatchObject({
+ state: {
+ data: 'Works from queryFn',
+ },
+ })
+ })
+})
diff --git a/packages/preact-query/src/__tests__/infiniteQueryOptions.test-d.tsx b/packages/preact-query/src/__tests__/infiniteQueryOptions.test-d.tsx
new file mode 100644
index 0000000000..9e0751f13c
--- /dev/null
+++ b/packages/preact-query/src/__tests__/infiniteQueryOptions.test-d.tsx
@@ -0,0 +1,252 @@
+import { QueryClient, dataTagSymbol, skipToken } from '@tanstack/query-core'
+import type {
+ DataTag,
+ InfiniteData,
+ InitialDataFunction,
+} from '@tanstack/query-core'
+import { assertType, describe, expectTypeOf, it, test } from 'vitest'
+
+import { infiniteQueryOptions } from '../infiniteQueryOptions'
+import { useInfiniteQuery } from '../useInfiniteQuery'
+import { useQuery } from '../useQuery'
+import { useSuspenseInfiniteQuery } from '../useSuspenseInfiniteQuery'
+
+describe('infiniteQueryOptions', () => {
+ it('should not allow excess properties', () => {
+ assertType(
+ infiniteQueryOptions({
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve('data'),
+ getNextPageParam: () => 1,
+ initialPageParam: 1,
+ // @ts-expect-error this is a good error, because stallTime does not exist!
+ stallTime: 1000,
+ }),
+ )
+ })
+ it('should infer types for callbacks', () => {
+ infiniteQueryOptions({
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve('data'),
+ staleTime: 1000,
+ getNextPageParam: () => 1,
+ initialPageParam: 1,
+ select: (data) => {
+ expectTypeOf(data).toEqualTypeOf>()
+ },
+ })
+ })
+ it('should work when passed to useInfiniteQuery', () => {
+ const options = infiniteQueryOptions({
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve('string'),
+ getNextPageParam: () => 1,
+ initialPageParam: 1,
+ })
+
+ const { data } = useInfiniteQuery(options)
+
+ // known issue: type of pageParams is unknown when returned from useInfiniteQuery
+ expectTypeOf(data).toEqualTypeOf<
+ InfiniteData | undefined
+ >()
+ })
+ it('should work when passed to useSuspenseInfiniteQuery', () => {
+ const options = infiniteQueryOptions({
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve('string'),
+ getNextPageParam: () => 1,
+ initialPageParam: 1,
+ })
+
+ const { data } = useSuspenseInfiniteQuery(options)
+
+ expectTypeOf(data).toEqualTypeOf>()
+ })
+ it('should work when passed to fetchInfiniteQuery', async () => {
+ const options = infiniteQueryOptions({
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve('string'),
+ getNextPageParam: () => 1,
+ initialPageParam: 1,
+ })
+
+ const data = await new QueryClient().fetchInfiniteQuery(options)
+
+ expectTypeOf(data).toEqualTypeOf>()
+ })
+ it('should tag the queryKey with the result type of the QueryFn', () => {
+ const { queryKey } = infiniteQueryOptions({
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve('string'),
+ getNextPageParam: () => 1,
+ initialPageParam: 1,
+ })
+
+ expectTypeOf(queryKey[dataTagSymbol]).toEqualTypeOf>()
+ })
+ it('should tag the queryKey even if no promise is returned', () => {
+ const { queryKey } = infiniteQueryOptions({
+ queryKey: ['key'],
+ queryFn: () => 'string',
+ getNextPageParam: () => 1,
+ initialPageParam: 1,
+ })
+
+ expectTypeOf(queryKey[dataTagSymbol]).toEqualTypeOf>()
+ })
+ it('should tag the queryKey with the result type of the QueryFn if select is used', () => {
+ const { queryKey } = infiniteQueryOptions({
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve('string'),
+ select: (data) => data.pages,
+ getNextPageParam: () => 1,
+ initialPageParam: 1,
+ })
+
+ expectTypeOf(queryKey[dataTagSymbol]).toEqualTypeOf>()
+ })
+ it('should return the proper type when passed to getQueryData', () => {
+ const { queryKey } = infiniteQueryOptions({
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve('string'),
+ getNextPageParam: () => 1,
+ initialPageParam: 1,
+ })
+
+ const queryClient = new QueryClient()
+ const data = queryClient.getQueryData(queryKey)
+
+ expectTypeOf(data).toEqualTypeOf<
+ InfiniteData | undefined
+ >()
+ })
+ it('should properly type when passed to setQueryData', () => {
+ const { queryKey } = infiniteQueryOptions({
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve('string'),
+ getNextPageParam: () => 1,
+ initialPageParam: 1,
+ })
+
+ const queryClient = new QueryClient()
+ const data = queryClient.setQueryData(queryKey, (prev) => {
+ expectTypeOf(prev).toEqualTypeOf<
+ InfiniteData | undefined
+ >()
+ return prev
+ })
+
+ expectTypeOf(data).toEqualTypeOf<
+ InfiniteData | undefined
+ >()
+ })
+ it('should throw a type error when using queryFn with skipToken in a suspense query', () => {
+ const options = infiniteQueryOptions({
+ queryKey: ['key'],
+ queryFn:
+ Math.random() > 0.5 ? skipToken : () => Promise.resolve('string'),
+ getNextPageParam: () => 1,
+ initialPageParam: 1,
+ })
+ // @ts-expect-error TS2345
+ const { data } = useSuspenseInfiniteQuery(options)
+ expectTypeOf(data).toEqualTypeOf>()
+ })
+
+ test('should not be allowed to be passed to non-infinite query functions', () => {
+ const queryClient = new QueryClient()
+ const options = infiniteQueryOptions({
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve('string'),
+ getNextPageParam: () => 1,
+ initialPageParam: 1,
+ })
+ assertType(
+ // @ts-expect-error cannot pass infinite options to non-infinite query functions
+ useQuery(options),
+ )
+ assertType(
+ // @ts-expect-error cannot pass infinite options to non-infinite query functions
+ queryClient.ensureQueryData(options),
+ )
+ assertType(
+ // @ts-expect-error cannot pass infinite options to non-infinite query functions
+ queryClient.fetchQuery(options),
+ )
+ assertType(
+ // @ts-expect-error cannot pass infinite options to non-infinite query functions
+ queryClient.prefetchQuery(options),
+ )
+ })
+
+ test('allow optional initialData function', () => {
+ const initialData: { example: boolean } | undefined = { example: true }
+ const queryOptions = infiniteQueryOptions({
+ queryKey: ['example'],
+ queryFn: () => initialData,
+ initialData: initialData
+ ? () => ({ pages: [initialData], pageParams: [] })
+ : undefined,
+ getNextPageParam: () => 1,
+ initialPageParam: 1,
+ })
+ expectTypeOf(queryOptions.initialData).toMatchTypeOf<
+ | InitialDataFunction>
+ | InfiniteData<{ example: boolean }, number>
+ | undefined
+ >()
+ })
+
+ test('allow optional initialData object', () => {
+ const initialData: { example: boolean } | undefined = { example: true }
+ const queryOptions = infiniteQueryOptions({
+ queryKey: ['example'],
+ queryFn: () => initialData,
+ initialData: initialData
+ ? { pages: [initialData], pageParams: [] }
+ : undefined,
+ getNextPageParam: () => 1,
+ initialPageParam: 1,
+ })
+ expectTypeOf(queryOptions.initialData).toMatchTypeOf<
+ | InitialDataFunction>
+ | InfiniteData<{ example: boolean }, number>
+ | undefined
+ >()
+ })
+
+ it('should return a custom query key type', () => {
+ type MyQueryKey = [Array, { type: 'foo' }]
+
+ const options = infiniteQueryOptions({
+ queryKey: [['key'], { type: 'foo' }] as MyQueryKey,
+ queryFn: () => Promise.resolve(1),
+ getNextPageParam: () => 1,
+ initialPageParam: 1,
+ })
+
+ expectTypeOf(options.queryKey).toEqualTypeOf<
+ DataTag, Error>
+ >()
+ })
+
+ it('should return a custom query key type with datatag', () => {
+ type MyQueryKey = DataTag<
+ [Array, { type: 'foo' }],
+ number,
+ Error & { myMessage: string }
+ >
+
+ const options = infiniteQueryOptions({
+ queryKey: [['key'], { type: 'foo' }] as MyQueryKey,
+ queryFn: () => Promise.resolve(1),
+ getNextPageParam: () => 1,
+ initialPageParam: 1,
+ })
+
+ expectTypeOf(options.queryKey).toEqualTypeOf<
+ DataTag, Error & { myMessage: string }>
+ >()
+ })
+})
diff --git a/packages/preact-query/src/__tests__/infiniteQueryOptions.test.tsx b/packages/preact-query/src/__tests__/infiniteQueryOptions.test.tsx
new file mode 100644
index 0000000000..3e876fd5d0
--- /dev/null
+++ b/packages/preact-query/src/__tests__/infiniteQueryOptions.test.tsx
@@ -0,0 +1,17 @@
+import { describe, expect, it } from 'vitest'
+
+import { infiniteQueryOptions } from '../infiniteQueryOptions'
+import type { UseInfiniteQueryOptions } from '../types'
+
+describe('infiniteQueryOptions', () => {
+ it('should return the object received as a parameter without any modification.', () => {
+ const object: UseInfiniteQueryOptions = {
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve(5),
+ getNextPageParam: () => null,
+ initialPageParam: null,
+ }
+
+ expect(infiniteQueryOptions(object)).toStrictEqual(object)
+ })
+})
diff --git a/packages/preact-query/src/__tests__/mutationOptions.test-d.tsx b/packages/preact-query/src/__tests__/mutationOptions.test-d.tsx
new file mode 100644
index 0000000000..b945477f8d
--- /dev/null
+++ b/packages/preact-query/src/__tests__/mutationOptions.test-d.tsx
@@ -0,0 +1,218 @@
+import { QueryClient } from '@tanstack/query-core'
+import type {
+ DefaultError,
+ MutationFunctionContext,
+ MutationState,
+ WithRequired,
+} from '@tanstack/query-core'
+import { assertType, describe, expectTypeOf, it } from 'vitest'
+
+import { useIsMutating, useMutation, useMutationState } from '..'
+import { mutationOptions } from '../mutationOptions'
+import type { UseMutationOptions, UseMutationResult } from '../types'
+
+describe('mutationOptions', () => {
+ it('should not allow excess properties', () => {
+ // @ts-expect-error this is a good error, because onMutates does not exist!
+ mutationOptions({
+ mutationFn: () => Promise.resolve(5),
+ mutationKey: ['key'],
+ onMutates: 1000,
+ onSuccess: (data) => {
+ expectTypeOf(data).toEqualTypeOf()
+ },
+ })
+ })
+
+ it('should infer types for callbacks', () => {
+ mutationOptions({
+ mutationFn: () => Promise.resolve(5),
+ mutationKey: ['key'],
+ onSuccess: (data) => {
+ expectTypeOf(data).toEqualTypeOf()
+ },
+ })
+ })
+
+ it('should infer types for onError callback', () => {
+ mutationOptions({
+ mutationFn: () => {
+ throw new Error('fail')
+ },
+ mutationKey: ['key'],
+ onError: (error) => {
+ expectTypeOf(error).toEqualTypeOf()
+ },
+ })
+ })
+
+ it('should infer types for variables', () => {
+ mutationOptions({
+ mutationFn: (vars) => {
+ expectTypeOf(vars).toEqualTypeOf<{ id: string }>()
+ return Promise.resolve(5)
+ },
+ mutationKey: ['with-vars'],
+ })
+ })
+
+ it('should infer result type correctly', () => {
+ mutationOptions({
+ mutationFn: () => Promise.resolve(5),
+ mutationKey: ['key'],
+ onMutate: () => {
+ return { name: 'onMutateResult' }
+ },
+ onSuccess: (_data, _variables, onMutateResult) => {
+ expectTypeOf(onMutateResult).toEqualTypeOf<{ name: string }>()
+ },
+ })
+ })
+
+ it('should infer context type correctly', () => {
+ mutationOptions({
+ mutationFn: (_variables, context) => {
+ expectTypeOf(context).toEqualTypeOf()
+ return Promise.resolve(5)
+ },
+ mutationKey: ['key'],
+ onMutate: (_variables, context) => {
+ expectTypeOf(context).toEqualTypeOf()
+ },
+ onSuccess: (_data, _variables, _onMutateResult, context) => {
+ expectTypeOf(context).toEqualTypeOf()
+ },
+ onError: (_error, _variables, _onMutateResult, context) => {
+ expectTypeOf(context).toEqualTypeOf()
+ },
+ onSettled: (_data, _error, _variables, _onMutateResult, context) => {
+ expectTypeOf(context).toEqualTypeOf()
+ },
+ })
+ })
+
+ it('should error if mutationFn return type mismatches TData', () => {
+ assertType(
+ mutationOptions({
+ // @ts-expect-error this is a good error, because return type is string, not number
+ mutationFn: async () => Promise.resolve('wrong return'),
+ }),
+ )
+ })
+
+ it('should allow mutationKey to be omitted', () => {
+ return mutationOptions({
+ mutationFn: () => Promise.resolve(123),
+ onSuccess: (data) => {
+ expectTypeOf(data).toEqualTypeOf()
+ },
+ })
+ })
+
+ it('should infer all types when not explicitly provided', () => {
+ expectTypeOf(
+ mutationOptions({
+ mutationFn: (id: string) => Promise.resolve(id.length),
+ mutationKey: ['key'],
+ onSuccess: (data) => {
+ expectTypeOf(data).toEqualTypeOf()
+ },
+ }),
+ ).toEqualTypeOf<
+ WithRequired<
+ UseMutationOptions,
+ 'mutationKey'
+ >
+ >()
+ expectTypeOf(
+ mutationOptions({
+ mutationFn: (id: string) => Promise.resolve(id.length),
+ onSuccess: (data) => {
+ expectTypeOf(data).toEqualTypeOf()
+ },
+ }),
+ ).toEqualTypeOf<
+ Omit, 'mutationKey'>
+ >()
+ })
+
+ it('should infer types when used with useMutation', () => {
+ const mutation = useMutation(
+ mutationOptions({
+ mutationKey: ['key'],
+ mutationFn: () => Promise.resolve('data'),
+ onSuccess: (data) => {
+ expectTypeOf(data).toEqualTypeOf()
+ },
+ }),
+ )
+ expectTypeOf(mutation).toEqualTypeOf<
+ UseMutationResult
+ >()
+
+ useMutation(
+ // should allow when used with useMutation without mutationKey
+ mutationOptions({
+ mutationFn: () => Promise.resolve('data'),
+ onSuccess: (data) => {
+ expectTypeOf(data).toEqualTypeOf()
+ },
+ }),
+ )
+ })
+
+ it('should infer types when used with useIsMutating', () => {
+ const isMutating = useIsMutating(
+ mutationOptions({
+ mutationKey: ['key'],
+ mutationFn: () => Promise.resolve(5),
+ }),
+ )
+ expectTypeOf(isMutating).toEqualTypeOf()
+
+ useIsMutating(
+ // @ts-expect-error filters should have mutationKey
+ mutationOptions({
+ mutationFn: () => Promise.resolve(5),
+ }),
+ )
+ })
+
+ it('should infer types when used with queryClient.isMutating', () => {
+ const queryClient = new QueryClient()
+
+ const isMutating = queryClient.isMutating(
+ mutationOptions({
+ mutationKey: ['key'],
+ mutationFn: () => Promise.resolve(5),
+ }),
+ )
+ expectTypeOf(isMutating).toEqualTypeOf()
+
+ queryClient.isMutating(
+ // @ts-expect-error filters should have mutationKey
+ mutationOptions({
+ mutationFn: () => Promise.resolve(5),
+ }),
+ )
+ })
+
+ it('should infer types when used with useMutationState', () => {
+ const mutationState = useMutationState({
+ filters: mutationOptions({
+ mutationKey: ['key'],
+ mutationFn: () => Promise.resolve(5),
+ }),
+ })
+ expectTypeOf(mutationState).toEqualTypeOf<
+ Array>
+ >()
+
+ useMutationState({
+ // @ts-expect-error filters should have mutationKey
+ filters: mutationOptions({
+ mutationFn: () => Promise.resolve(5),
+ }),
+ })
+ })
+})
diff --git a/packages/preact-query/src/__tests__/mutationOptions.test.tsx b/packages/preact-query/src/__tests__/mutationOptions.test.tsx
new file mode 100644
index 0000000000..91fedeb64c
--- /dev/null
+++ b/packages/preact-query/src/__tests__/mutationOptions.test.tsx
@@ -0,0 +1,527 @@
+import { QueryClient } from '@tanstack/query-core'
+import type { MutationState } from '@tanstack/query-core'
+import { sleep } from '@tanstack/query-test-utils'
+import { fireEvent } from '@testing-library/preact'
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
+
+import { useIsMutating, useMutation, useMutationState } from '..'
+import { mutationOptions } from '../mutationOptions'
+import { renderWithClient } from './utils'
+
+describe('mutationOptions', () => {
+ beforeEach(() => {
+ vi.useFakeTimers()
+ })
+
+ afterEach(() => {
+ vi.useRealTimers()
+ })
+
+ it('should return the object received as a parameter without any modification (with mutationKey in mutationOptions)', () => {
+ const object = {
+ mutationKey: ['key'],
+ mutationFn: () => sleep(10).then(() => 5),
+ } as const
+
+ expect(mutationOptions(object)).toStrictEqual(object)
+ })
+
+ it('should return the object received as a parameter without any modification (without mutationKey in mutationOptions)', () => {
+ const object = {
+ mutationFn: () => sleep(10).then(() => 5),
+ } as const
+
+ expect(mutationOptions(object)).toStrictEqual(object)
+ })
+
+ it('should return the number of fetching mutations when used with useIsMutating (with mutationKey in mutationOptions)', async () => {
+ const isMutatingArray: Array = []
+ const queryClient = new QueryClient()
+ const mutationOpts = mutationOptions({
+ mutationKey: ['key'],
+ mutationFn: () => sleep(50).then(() => 'data'),
+ })
+
+ function IsMutating() {
+ const isMutating = useIsMutating()
+
+ isMutatingArray.push(isMutating)
+
+ return null
+ }
+
+ function Mutation() {
+ const { mutate } = useMutation(mutationOpts)
+
+ return (
+
+ mutate()}>mutate
+
+ )
+ }
+
+ function Page() {
+ return (
+
+
+
+
+ )
+ }
+
+ const rendered = renderWithClient(queryClient, )
+
+ fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
+ expect(isMutatingArray[0]).toEqual(0)
+ await vi.advanceTimersByTimeAsync(0)
+ expect(isMutatingArray[1]).toEqual(1)
+ await vi.advanceTimersByTimeAsync(51)
+ expect(isMutatingArray[2]).toEqual(0)
+ expect(isMutatingArray[isMutatingArray.length - 1]).toEqual(0)
+ })
+
+ it('should return the number of fetching mutations when used with useIsMutating (without mutationKey in mutationOptions)', async () => {
+ const isMutatingArray: Array = []
+ const queryClient = new QueryClient()
+ const mutationOpts = mutationOptions({
+ mutationFn: () => sleep(50).then(() => 'data'),
+ })
+
+ function IsMutating() {
+ const isMutating = useIsMutating()
+
+ isMutatingArray.push(isMutating)
+
+ return null
+ }
+
+ function Mutation() {
+ const { mutate } = useMutation(mutationOpts)
+
+ return (
+
+ mutate()}>mutate
+
+ )
+ }
+
+ function Page() {
+ return (
+
+
+
+
+ )
+ }
+
+ const rendered = renderWithClient(queryClient, )
+
+ fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
+ expect(isMutatingArray[0]).toEqual(0)
+ await vi.advanceTimersByTimeAsync(0)
+ expect(isMutatingArray[1]).toEqual(1)
+ await vi.advanceTimersByTimeAsync(51)
+ expect(isMutatingArray[2]).toEqual(0)
+ expect(isMutatingArray[isMutatingArray.length - 1]).toEqual(0)
+ })
+
+ it('should return the number of fetching mutations when used with useIsMutating', async () => {
+ const isMutatingArray: Array = []
+ const queryClient = new QueryClient()
+ const mutationOpts1 = mutationOptions({
+ mutationKey: ['key'],
+ mutationFn: () => sleep(50).then(() => 'data1'),
+ })
+ const mutationOpts2 = mutationOptions({
+ mutationFn: () => sleep(50).then(() => 'data2'),
+ })
+
+ function IsMutating() {
+ const isMutating = useIsMutating()
+
+ isMutatingArray.push(isMutating)
+
+ return null
+ }
+
+ function Mutation() {
+ const { mutate: mutate1 } = useMutation(mutationOpts1)
+ const { mutate: mutate2 } = useMutation(mutationOpts2)
+
+ return (
+
+ mutate1()}>mutate1
+ mutate2()}>mutate2
+
+ )
+ }
+
+ function Page() {
+ return (
+
+
+
+
+ )
+ }
+
+ const rendered = renderWithClient(queryClient, )
+
+ fireEvent.click(rendered.getByRole('button', { name: /mutate1/i }))
+ fireEvent.click(rendered.getByRole('button', { name: /mutate2/i }))
+ expect(isMutatingArray[0]).toEqual(0)
+ await vi.advanceTimersByTimeAsync(0)
+ expect(isMutatingArray[1]).toEqual(2)
+ await vi.advanceTimersByTimeAsync(51)
+ expect(isMutatingArray[2]).toEqual(0)
+ expect(isMutatingArray[isMutatingArray.length - 1]).toEqual(0)
+ })
+
+ it('should return the number of fetching mutations when used with useIsMutating (filter mutationOpts1.mutationKey)', async () => {
+ const isMutatingArray: Array = []
+ const queryClient = new QueryClient()
+ const mutationOpts1 = mutationOptions({
+ mutationKey: ['key'],
+ mutationFn: () => sleep(50).then(() => 'data1'),
+ })
+ const mutationOpts2 = mutationOptions({
+ mutationFn: () => sleep(50).then(() => 'data2'),
+ })
+
+ function IsMutating() {
+ const isMutating = useIsMutating({
+ mutationKey: mutationOpts1.mutationKey,
+ })
+
+ isMutatingArray.push(isMutating)
+
+ return null
+ }
+
+ function Mutation() {
+ const { mutate: mutate1 } = useMutation(mutationOpts1)
+ const { mutate: mutate2 } = useMutation(mutationOpts2)
+
+ return (
+
+ mutate1()}>mutate1
+ mutate2()}>mutate2
+
+ )
+ }
+
+ function Page() {
+ return (
+
+
+
+
+ )
+ }
+
+ const rendered = renderWithClient(queryClient, )
+
+ fireEvent.click(rendered.getByRole('button', { name: /mutate1/i }))
+ fireEvent.click(rendered.getByRole('button', { name: /mutate2/i }))
+ expect(isMutatingArray[0]).toEqual(0)
+ await vi.advanceTimersByTimeAsync(0)
+ expect(isMutatingArray[1]).toEqual(1)
+ await vi.advanceTimersByTimeAsync(51)
+ expect(isMutatingArray[2]).toEqual(0)
+ expect(isMutatingArray[isMutatingArray.length - 1]).toEqual(0)
+ })
+
+ it('should return the number of fetching mutations when used with queryClient.isMutating (with mutationKey in mutationOptions)', async () => {
+ const isMutatingArray: Array = []
+ const queryClient = new QueryClient()
+ const mutationOpts = mutationOptions({
+ mutationKey: ['mutation'],
+ mutationFn: () => sleep(500).then(() => 'data'),
+ })
+
+ function Mutation() {
+ const isMutating = queryClient.isMutating(mutationOpts)
+ const { mutate } = useMutation(mutationOpts)
+
+ isMutatingArray.push(isMutating)
+
+ return (
+
+ mutate()}>mutate
+
+ )
+ }
+
+ const rendered = renderWithClient(queryClient, )
+
+ fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
+ expect(isMutatingArray[0]).toEqual(0)
+ await vi.advanceTimersByTimeAsync(0)
+ expect(isMutatingArray[1]).toEqual(1)
+ await vi.advanceTimersByTimeAsync(501)
+ expect(isMutatingArray[2]).toEqual(0)
+ expect(isMutatingArray[isMutatingArray.length - 1]).toEqual(0)
+ })
+
+ it('should return the number of fetching mutations when used with queryClient.isMutating (without mutationKey in mutationOptions)', async () => {
+ const isMutatingArray: Array = []
+ const queryClient = new QueryClient()
+ const mutationOpts = mutationOptions({
+ mutationFn: () => sleep(500).then(() => 'data'),
+ })
+
+ function Mutation() {
+ const isMutating = queryClient.isMutating()
+ const { mutate } = useMutation(mutationOpts)
+
+ isMutatingArray.push(isMutating)
+
+ return (
+
+ mutate()}>mutate
+
+ )
+ }
+
+ const rendered = renderWithClient(queryClient, )
+
+ fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
+ expect(isMutatingArray[0]).toEqual(0)
+ await vi.advanceTimersByTimeAsync(0)
+ expect(isMutatingArray[1]).toEqual(1)
+ await vi.advanceTimersByTimeAsync(501)
+ expect(isMutatingArray[2]).toEqual(0)
+ expect(isMutatingArray[isMutatingArray.length - 1]).toEqual(0)
+ })
+
+ it('should return the number of fetching mutations when used with queryClient.isMutating', async () => {
+ const isMutatingArray: Array = []
+ const queryClient = new QueryClient()
+ const mutationOpts1 = mutationOptions({
+ mutationKey: ['mutation'],
+ mutationFn: () => sleep(500).then(() => 'data1'),
+ })
+ const mutationOpts2 = mutationOptions({
+ mutationFn: () => sleep(500).then(() => 'data2'),
+ })
+
+ function Mutation() {
+ const isMutating = queryClient.isMutating()
+ const { mutate: mutate1 } = useMutation(mutationOpts1)
+ const { mutate: mutate2 } = useMutation(mutationOpts2)
+
+ isMutatingArray.push(isMutating)
+
+ return (
+
+ mutate1()}>mutate1
+ mutate2()}>mutate2
+
+ )
+ }
+
+ const rendered = renderWithClient(queryClient, )
+
+ fireEvent.click(rendered.getByRole('button', { name: /mutate1/i }))
+ fireEvent.click(rendered.getByRole('button', { name: /mutate2/i }))
+ expect(isMutatingArray[0]).toEqual(0)
+ await vi.advanceTimersByTimeAsync(0)
+ expect(isMutatingArray[1]).toEqual(2)
+ await vi.advanceTimersByTimeAsync(501)
+ expect(isMutatingArray[2]).toEqual(0)
+ expect(isMutatingArray[isMutatingArray.length - 1]).toEqual(0)
+ })
+
+ it('should return the number of fetching mutations when used with queryClient.isMutating (filter mutationOpt1.mutationKey)', async () => {
+ const isMutatingArray: Array = []
+ const queryClient = new QueryClient()
+ const mutationOpts1 = mutationOptions({
+ mutationKey: ['mutation'],
+ mutationFn: () => sleep(500).then(() => 'data1'),
+ })
+ const mutationOpts2 = mutationOptions({
+ mutationFn: () => sleep(500).then(() => 'data2'),
+ })
+
+ function Mutation() {
+ const isMutating = queryClient.isMutating({
+ mutationKey: mutationOpts1.mutationKey,
+ })
+ const { mutate: mutate1 } = useMutation(mutationOpts1)
+ const { mutate: mutate2 } = useMutation(mutationOpts2)
+
+ isMutatingArray.push(isMutating)
+
+ return (
+
+ mutate1()}>mutate1
+ mutate2()}>mutate2
+
+ )
+ }
+
+ const rendered = renderWithClient(queryClient, )
+
+ fireEvent.click(rendered.getByRole('button', { name: /mutate1/i }))
+ fireEvent.click(rendered.getByRole('button', { name: /mutate2/i }))
+ expect(isMutatingArray[0]).toEqual(0)
+ await vi.advanceTimersByTimeAsync(0)
+ expect(isMutatingArray[1]).toEqual(1)
+ await vi.advanceTimersByTimeAsync(501)
+ expect(isMutatingArray[2]).toEqual(0)
+ expect(isMutatingArray[isMutatingArray.length - 1]).toEqual(0)
+ })
+
+ it('should return the number of fetching mutations when used with useMutationState (with mutationKey in mutationOptions)', async () => {
+ const mutationStateArray: Array<
+ MutationState
+ > = []
+ const queryClient = new QueryClient()
+ const mutationOpts = mutationOptions({
+ mutationKey: ['mutation'],
+ mutationFn: () => sleep(10).then(() => 'data'),
+ })
+
+ function Mutation() {
+ const { mutate } = useMutation(mutationOpts)
+ const data = useMutationState({
+ filters: { mutationKey: mutationOpts.mutationKey, status: 'success' },
+ })
+
+ mutationStateArray.push(...data)
+
+ return (
+
+ mutate()}>mutate
+
+ )
+ }
+
+ const rendered = renderWithClient(queryClient, )
+
+ expect(mutationStateArray.length).toEqual(0)
+
+ fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
+ await vi.advanceTimersByTimeAsync(11)
+ expect(mutationStateArray.length).toEqual(1)
+ expect(mutationStateArray[0]?.data).toEqual('data')
+ })
+
+ it('should return the number of fetching mutations when used with useMutationState (without mutationKey in mutationOptions)', async () => {
+ const mutationStateArray: Array<
+ MutationState
+ > = []
+ const queryClient = new QueryClient()
+ const mutationOpts = mutationOptions({
+ mutationFn: () => sleep(10).then(() => 'data'),
+ })
+
+ function Mutation() {
+ const { mutate } = useMutation(mutationOpts)
+ const data = useMutationState({
+ filters: { status: 'success' },
+ })
+
+ mutationStateArray.push(...data)
+
+ return (
+
+ mutate()}>mutate
+
+ )
+ }
+
+ const rendered = renderWithClient(queryClient, )
+
+ expect(mutationStateArray.length).toEqual(0)
+
+ fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
+ await vi.advanceTimersByTimeAsync(11)
+ expect(mutationStateArray.length).toEqual(1)
+ expect(mutationStateArray[0]?.data).toEqual('data')
+ })
+
+ it('should return the number of fetching mutations when used with useMutationState', async () => {
+ const mutationStateArray: Array<
+ MutationState
+ > = []
+ const queryClient = new QueryClient()
+ const mutationOpts1 = mutationOptions({
+ mutationKey: ['mutation'],
+ mutationFn: () => sleep(10).then(() => 'data1'),
+ })
+ const mutationOpts2 = mutationOptions({
+ mutationFn: () => sleep(10).then(() => 'data2'),
+ })
+
+ function Mutation() {
+ const { mutate: mutate1 } = useMutation(mutationOpts1)
+ const { mutate: mutate2 } = useMutation(mutationOpts2)
+ const data = useMutationState({
+ filters: { status: 'success' },
+ })
+
+ mutationStateArray.push(...data)
+
+ return (
+
+ mutate1()}>mutate1
+ mutate2()}>mutate2
+
+ )
+ }
+
+ const rendered = renderWithClient(queryClient, )
+
+ expect(mutationStateArray.length).toEqual(0)
+
+ fireEvent.click(rendered.getByRole('button', { name: /mutate1/i }))
+ fireEvent.click(rendered.getByRole('button', { name: /mutate2/i }))
+ await vi.advanceTimersByTimeAsync(11)
+ expect(mutationStateArray.length).toEqual(2)
+ expect(mutationStateArray[0]?.data).toEqual('data1')
+ expect(mutationStateArray[1]?.data).toEqual('data2')
+ })
+
+ it('should return the number of fetching mutations when used with useMutationState (filter mutationOpt1.mutationKey)', async () => {
+ const mutationStateArray: Array<
+ MutationState
+ > = []
+ const queryClient = new QueryClient()
+ const mutationOpts1 = mutationOptions({
+ mutationKey: ['mutation'],
+ mutationFn: () => sleep(10).then(() => 'data1'),
+ })
+ const mutationOpts2 = mutationOptions({
+ mutationFn: () => sleep(10).then(() => 'data2'),
+ })
+
+ function Mutation() {
+ const { mutate: mutate1 } = useMutation(mutationOpts1)
+ const { mutate: mutate2 } = useMutation(mutationOpts2)
+ const data = useMutationState({
+ filters: { mutationKey: mutationOpts1.mutationKey, status: 'success' },
+ })
+
+ mutationStateArray.push(...data)
+
+ return (
+
+ mutate1()}>mutate1
+ mutate2()}>mutate2
+
+ )
+ }
+
+ const rendered = renderWithClient(queryClient, )
+
+ expect(mutationStateArray.length).toEqual(0)
+
+ fireEvent.click(rendered.getByRole('button', { name: /mutate1/i }))
+ fireEvent.click(rendered.getByRole('button', { name: /mutate2/i }))
+ await vi.advanceTimersByTimeAsync(11)
+ expect(mutationStateArray.length).toEqual(1)
+ expect(mutationStateArray[0]?.data).toEqual('data1')
+ expect(mutationStateArray[1]).toBeFalsy()
+ })
+})
diff --git a/packages/preact-query/src/__tests__/queryOptions.test-d.tsx b/packages/preact-query/src/__tests__/queryOptions.test-d.tsx
new file mode 100644
index 0000000000..199400b6b3
--- /dev/null
+++ b/packages/preact-query/src/__tests__/queryOptions.test-d.tsx
@@ -0,0 +1,287 @@
+import {
+ QueriesObserver,
+ QueryClient,
+ dataTagSymbol,
+ skipToken,
+} from '@tanstack/query-core'
+import type {
+ DataTag,
+ InitialDataFunction,
+ QueryObserverResult,
+} from '@tanstack/query-core'
+import { assertType, describe, expectTypeOf, it } from 'vitest'
+
+import { queryOptions } from '../queryOptions'
+import type { AnyUseQueryOptions } from '../types'
+import { useQueries } from '../useQueries'
+import { useQuery } from '../useQuery'
+import { useSuspenseQuery } from '../useSuspenseQuery'
+
+describe('queryOptions', () => {
+ it('should not allow excess properties', () => {
+ assertType(
+ queryOptions({
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve(5),
+ // @ts-expect-error this is a good error, because stallTime does not exist!
+ stallTime: 1000,
+ }),
+ )
+ })
+ it('should infer types for callbacks', () => {
+ queryOptions({
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve(5),
+ staleTime: 1000,
+ select: (data) => {
+ expectTypeOf(data).toEqualTypeOf()
+ },
+ })
+ })
+ it('should work when passed to useQuery', () => {
+ const options = queryOptions({
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve(5),
+ })
+
+ const { data } = useQuery(options)
+ expectTypeOf(data).toEqualTypeOf()
+ })
+ it('should work when passed to useSuspenseQuery', () => {
+ const options = queryOptions({
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve(5),
+ })
+
+ const { data } = useSuspenseQuery(options)
+ expectTypeOf(data).toEqualTypeOf()
+ })
+
+ it('should work when passed to fetchQuery', async () => {
+ const options = queryOptions({
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve(5),
+ })
+
+ const data = await new QueryClient().fetchQuery(options)
+ expectTypeOf(data).toEqualTypeOf()
+ })
+ it('should work when passed to useQueries', () => {
+ const options = queryOptions({
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve(5),
+ })
+
+ const [{ data }] = useQueries({
+ queries: [options],
+ })
+
+ expectTypeOf(data).toEqualTypeOf()
+ })
+ it('should tag the queryKey with the result type of the QueryFn', () => {
+ const { queryKey } = queryOptions({
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve(5),
+ })
+
+ expectTypeOf(queryKey[dataTagSymbol]).toEqualTypeOf()
+ })
+ it('should tag the queryKey even if no promise is returned', () => {
+ const { queryKey } = queryOptions({
+ queryKey: ['key'],
+ queryFn: () => 5,
+ })
+
+ expectTypeOf(queryKey[dataTagSymbol]).toEqualTypeOf()
+ })
+ it('should tag the queryKey with unknown if there is no queryFn', () => {
+ const { queryKey } = queryOptions({
+ queryKey: ['key'],
+ })
+
+ expectTypeOf(queryKey[dataTagSymbol]).toEqualTypeOf()
+ })
+ it('should tag the queryKey with the result type of the QueryFn if select is used', () => {
+ const { queryKey } = queryOptions({
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve(5),
+ select: (data) => data.toString(),
+ })
+
+ expectTypeOf(queryKey[dataTagSymbol]).toEqualTypeOf()
+ })
+ it('should return the proper type when passed to getQueryData', () => {
+ const { queryKey } = queryOptions({
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve(5),
+ })
+
+ const queryClient = new QueryClient()
+ const data = queryClient.getQueryData(queryKey)
+ expectTypeOf(data).toEqualTypeOf()
+ })
+ it('should return the proper type when passed to getQueryState', () => {
+ const { queryKey } = queryOptions({
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve(5),
+ })
+
+ const queryClient = new QueryClient()
+ const state = queryClient.getQueryState(queryKey)
+ expectTypeOf(state?.data).toEqualTypeOf()
+ })
+ it('should properly type updaterFn when passed to setQueryData', () => {
+ const { queryKey } = queryOptions({
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve(5),
+ })
+
+ const queryClient = new QueryClient()
+ const data = queryClient.setQueryData(queryKey, (prev) => {
+ expectTypeOf(prev).toEqualTypeOf()
+ return prev
+ })
+ expectTypeOf(data).toEqualTypeOf()
+ })
+ it('should properly type value when passed to setQueryData', () => {
+ const { queryKey } = queryOptions({
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve(5),
+ })
+
+ const queryClient = new QueryClient()
+
+ // @ts-expect-error value should be a number
+ queryClient.setQueryData(queryKey, '5')
+ // @ts-expect-error value should be a number
+ queryClient.setQueryData(queryKey, () => '5')
+
+ const data = queryClient.setQueryData(queryKey, 5)
+ expectTypeOf(data).toEqualTypeOf()
+ })
+
+ it('should infer even if there is a conditional skipToken', () => {
+ const options = queryOptions({
+ queryKey: ['key'],
+ queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5),
+ })
+
+ const queryClient = new QueryClient()
+ const data = queryClient.getQueryData(options.queryKey)
+ expectTypeOf(data).toEqualTypeOf()
+ })
+
+ it('should infer to unknown if we disable a query with just a skipToken', () => {
+ const options = queryOptions({
+ queryKey: ['key'],
+ queryFn: skipToken,
+ })
+
+ const queryClient = new QueryClient()
+ const data = queryClient.getQueryData(options.queryKey)
+ expectTypeOf(data).toEqualTypeOf()
+ })
+
+ it('should throw a type error when using queryFn with skipToken in a suspense query', () => {
+ const options = queryOptions({
+ queryKey: ['key'],
+ queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5),
+ })
+ // @ts-expect-error TS2345
+ const { data } = useSuspenseQuery(options)
+ expectTypeOf(data).toEqualTypeOf()
+ })
+
+ it('should return the proper type when passed to QueriesObserver', () => {
+ const options = queryOptions({
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve(5),
+ })
+
+ const queryClient = new QueryClient()
+ const queriesObserver = new QueriesObserver(queryClient, [options])
+ expectTypeOf(queriesObserver).toEqualTypeOf<
+ QueriesObserver>
+ >()
+ })
+
+ it('should allow undefined response in initialData', () => {
+ assertType((id: string | null) =>
+ queryOptions({
+ queryKey: ['todo', id],
+ queryFn: () =>
+ Promise.resolve({
+ id: '1',
+ title: 'Do Laundry',
+ }),
+ initialData: () =>
+ !id
+ ? undefined
+ : {
+ id,
+ title: 'Initial Data',
+ },
+ }),
+ )
+ })
+
+ it('should allow optional initialData object', () => {
+ const testFn = (id?: string) => {
+ const options = queryOptions({
+ queryKey: ['test'],
+ queryFn: () => Promise.resolve('something string'),
+ initialData: id ? 'initial string' : undefined,
+ })
+ expectTypeOf(options.initialData).toMatchTypeOf<
+ InitialDataFunction | string | undefined
+ >()
+ }
+ testFn('id')
+ testFn()
+ })
+
+ it('should be passable to UseQueryOptions', () => {
+ function somethingWithQueryOptions(
+ options: TQueryOpts,
+ ) {
+ return options.queryKey
+ }
+
+ const options = queryOptions({
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve(1),
+ })
+
+ assertType(somethingWithQueryOptions(options))
+ })
+
+ it('should return a custom query key type', () => {
+ type MyQueryKey = [Array, { type: 'foo' }]
+
+ const options = queryOptions({
+ queryKey: [['key'], { type: 'foo' }] as MyQueryKey,
+ queryFn: () => Promise.resolve(1),
+ })
+
+ expectTypeOf(options.queryKey).toEqualTypeOf<
+ DataTag
+ >()
+ })
+
+ it('should return a custom query key type with datatag', () => {
+ type MyQueryKey = DataTag<
+ [Array, { type: 'foo' }],
+ number,
+ Error & { myMessage: string }
+ >
+
+ const options = queryOptions({
+ queryKey: [['key'], { type: 'foo' }] as MyQueryKey,
+ queryFn: () => Promise.resolve(1),
+ })
+
+ expectTypeOf(options.queryKey).toEqualTypeOf<
+ DataTag
+ >()
+ })
+})
diff --git a/packages/preact-query/src/__tests__/queryOptions.test.tsx b/packages/preact-query/src/__tests__/queryOptions.test.tsx
new file mode 100644
index 0000000000..73aa7c128e
--- /dev/null
+++ b/packages/preact-query/src/__tests__/queryOptions.test.tsx
@@ -0,0 +1,15 @@
+import { describe, expect, it } from 'vitest'
+
+import { queryOptions } from '../queryOptions'
+import type { UseQueryOptions } from '../types'
+
+describe('queryOptions', () => {
+ it('should return the object received as a parameter without any modification.', () => {
+ const object: UseQueryOptions = {
+ queryKey: ['key'],
+ queryFn: () => Promise.resolve(5),
+ } as const
+
+ expect(queryOptions(object)).toStrictEqual(object)
+ })
+})
diff --git a/packages/preact-query/src/__tests__/ssr-hydration.test.tsx b/packages/preact-query/src/__tests__/ssr-hydration.test.tsx
new file mode 100644
index 0000000000..10154d0189
--- /dev/null
+++ b/packages/preact-query/src/__tests__/ssr-hydration.test.tsx
@@ -0,0 +1,268 @@
+import { act } from '@testing-library/preact'
+import { hydrate as preactHydrate, render } from 'preact'
+import type { VNode } from 'preact'
+import { renderToString } from 'preact-render-to-string'
+import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'
+
+import {
+ QueryCache,
+ QueryClient,
+ QueryClientProvider,
+ dehydrate,
+ hydrate,
+ useQuery,
+} from '..'
+import { setIsServer } from './utils'
+
+const PreactHydrate = (element: VNode, container: Element) => {
+ act(() => {
+ preactHydrate(element, container)
+ })
+ // To unmount in Preact, you render null into the same container
+ return () => {
+ act(() => {
+ render(null, container)
+ })
+ }
+}
+
+async function fetchData(value: TData, ms?: number): Promise {
+ await vi.advanceTimersByTimeAsync(ms || 1)
+ return value
+}
+
+function PrintStateComponent({ componentName, result }: any): any {
+ return `${componentName} - status:${result.status} fetching:${result.isFetching} data:${result.data}`
+}
+
+describe('Server side rendering with de/rehydration', () => {
+ beforeAll(() => {
+ vi.useFakeTimers()
+ })
+
+ afterAll(() => {
+ vi.useRealTimers()
+ })
+
+ it('should not mismatch on success', async () => {
+ const consoleMock = vi.spyOn(console, 'error')
+ consoleMock.mockImplementation(() => undefined)
+
+ const fetchDataSuccess = vi.fn(fetchData)
+
+ // -- Shared part --
+ function SuccessComponent() {
+ const result = useQuery({
+ queryKey: ['success'],
+ queryFn: () => fetchDataSuccess('success!'),
+ })
+ return (
+
+ )
+ }
+
+ // -- Server part --
+ setIsServer(true)
+
+ const prefetchCache = new QueryCache()
+ const prefetchClient = new QueryClient({
+ queryCache: prefetchCache,
+ })
+ await prefetchClient.prefetchQuery({
+ queryKey: ['success'],
+ queryFn: () => fetchDataSuccess('success'),
+ })
+ const dehydratedStateServer = dehydrate(prefetchClient)
+ const renderCache = new QueryCache()
+ const renderClient = new QueryClient({
+ queryCache: renderCache,
+ })
+ hydrate(renderClient, dehydratedStateServer)
+ const markup = renderToString(
+
+
+ ,
+ )
+ const stringifiedState = JSON.stringify(dehydratedStateServer)
+ renderClient.clear()
+ setIsServer(false)
+
+ const expectedMarkup =
+ 'SuccessComponent - status:success fetching:true data:success'
+
+ expect(markup).toBe(expectedMarkup)
+ expect(fetchDataSuccess).toHaveBeenCalledTimes(1)
+
+ // -- Client part --
+ const el = document.createElement('div')
+ el.innerHTML = markup
+
+ const queryCache = new QueryCache()
+ const queryClient = new QueryClient({ queryCache })
+ hydrate(queryClient, JSON.parse(stringifiedState))
+
+ const unmount = PreactHydrate(
+
+
+ ,
+ el,
+ )
+
+ // Check that we have no React hydration mismatches
+ expect(consoleMock).toHaveBeenCalledTimes(0)
+
+ expect(fetchDataSuccess).toHaveBeenCalledTimes(2)
+ expect(el.innerHTML).toBe(expectedMarkup)
+
+ unmount()
+ queryClient.clear()
+ consoleMock.mockRestore()
+ })
+
+ it('should not mismatch on error', async () => {
+ const consoleMock = vi.spyOn(console, 'error')
+ consoleMock.mockImplementation(() => undefined)
+
+ const fetchDataError = vi.fn(() => {
+ throw new Error('fetchDataError')
+ })
+
+ // -- Shared part --
+ function ErrorComponent() {
+ const result = useQuery({
+ queryKey: ['error'],
+ queryFn: () => fetchDataError(),
+ retry: false,
+ })
+ return (
+
+ )
+ }
+
+ // -- Server part --
+ setIsServer(true)
+ const prefetchCache = new QueryCache()
+ const prefetchClient = new QueryClient({
+ queryCache: prefetchCache,
+ })
+ await prefetchClient.prefetchQuery({
+ queryKey: ['error'],
+ queryFn: () => fetchDataError(),
+ })
+ const dehydratedStateServer = dehydrate(prefetchClient)
+ const renderCache = new QueryCache()
+ const renderClient = new QueryClient({
+ queryCache: renderCache,
+ })
+ hydrate(renderClient, dehydratedStateServer)
+ const markup = renderToString(
+
+
+ ,
+ )
+ const stringifiedState = JSON.stringify(dehydratedStateServer)
+ renderClient.clear()
+ setIsServer(false)
+
+ const expectedMarkup =
+ 'ErrorComponent - status:pending fetching:true data:undefined'
+
+ expect(markup).toBe(expectedMarkup)
+
+ // -- Client part --
+ const el = document.createElement('div')
+ el.innerHTML = markup
+
+ const queryCache = new QueryCache()
+ const queryClient = new QueryClient({ queryCache })
+ hydrate(queryClient, JSON.parse(stringifiedState))
+
+ const unmount = PreactHydrate(
+
+
+ ,
+ el,
+ )
+
+ expect(consoleMock).toHaveBeenCalledTimes(0)
+ expect(fetchDataError).toHaveBeenCalledTimes(2)
+ expect(el.innerHTML).toBe(expectedMarkup)
+ await vi.advanceTimersByTimeAsync(50)
+ expect(fetchDataError).toHaveBeenCalledTimes(2)
+ expect(el.innerHTML).toBe(
+ 'ErrorComponent - status:error fetching:false data:undefined',
+ )
+
+ unmount()
+ queryClient.clear()
+ consoleMock.mockRestore()
+ })
+
+ it('should not mismatch on queries that were not prefetched', async () => {
+ const consoleMock = vi.spyOn(console, 'error')
+ consoleMock.mockImplementation(() => undefined)
+
+ const fetchDataSuccess = vi.fn(fetchData)
+
+ // -- Shared part --
+ function SuccessComponent() {
+ const result = useQuery({
+ queryKey: ['success'],
+ queryFn: () => fetchDataSuccess('success!'),
+ })
+ return (
+
+ )
+ }
+
+ // -- Server part --
+ setIsServer(true)
+
+ const prefetchClient = new QueryClient()
+ const dehydratedStateServer = dehydrate(prefetchClient)
+ const renderClient = new QueryClient()
+ hydrate(renderClient, dehydratedStateServer)
+ const markup = renderToString(
+
+
+ ,
+ )
+ const stringifiedState = JSON.stringify(dehydratedStateServer)
+ renderClient.clear()
+ setIsServer(false)
+
+ const expectedMarkup =
+ 'SuccessComponent - status:pending fetching:true data:undefined'
+
+ expect(markup).toBe(expectedMarkup)
+
+ // -- Client part --
+ const el = document.createElement('div')
+ el.innerHTML = markup
+
+ const queryCache = new QueryCache()
+ const queryClient = new QueryClient({ queryCache })
+ hydrate(queryClient, JSON.parse(stringifiedState))
+
+ const unmount = PreactHydrate(
+
+
+ ,
+ el,
+ )
+
+ // Check that we have no React hydration mismatches
+ expect(consoleMock).toHaveBeenCalledTimes(0)
+ expect(fetchDataSuccess).toHaveBeenCalledTimes(1)
+ expect(el.innerHTML).toBe(expectedMarkup)
+ await vi.advanceTimersByTimeAsync(50)
+ expect(fetchDataSuccess).toHaveBeenCalledTimes(1)
+ expect(el.innerHTML).toBe(
+ 'SuccessComponent - status:success fetching:false data:success!',
+ )
+
+ unmount()
+ queryClient.clear()
+ consoleMock.mockRestore()
+ })
+})
diff --git a/packages/preact-query/src/__tests__/ssr.test.tsx b/packages/preact-query/src/__tests__/ssr.test.tsx
new file mode 100644
index 0000000000..d365c474ae
--- /dev/null
+++ b/packages/preact-query/src/__tests__/ssr.test.tsx
@@ -0,0 +1,177 @@
+import { queryKey, sleep } from '@tanstack/query-test-utils'
+import { renderToString } from 'preact-render-to-string'
+import { useState } from 'preact/hooks'
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
+
+import {
+ QueryCache,
+ QueryClient,
+ QueryClientProvider,
+ useInfiniteQuery,
+ useQuery,
+} from '..'
+import { setIsServer } from './utils'
+
+describe('Server Side Rendering', () => {
+ setIsServer(true)
+
+ let queryCache: QueryCache
+ let queryClient: QueryClient
+
+ beforeEach(() => {
+ vi.useFakeTimers()
+ queryCache = new QueryCache()
+ queryClient = new QueryClient({ queryCache })
+ })
+
+ afterEach(() => {
+ vi.useRealTimers()
+ })
+
+ it('should not trigger fetch', () => {
+ const key = queryKey()
+ const queryFn = vi.fn(() => sleep(10).then(() => 'data'))
+
+ function Page() {
+ const query = useQuery({ queryKey: key, queryFn })
+
+ const content = `status ${query.status}`
+
+ return (
+
+ )
+ }
+
+ const markup = renderToString(
+
+
+ ,
+ )
+
+ expect(markup).toContain('status pending')
+ expect(queryFn).toHaveBeenCalledTimes(0)
+
+ queryCache.clear()
+ })
+
+ it('should add prefetched data to cache', async () => {
+ const key = queryKey()
+
+ const promise = queryClient.fetchQuery({
+ queryKey: key,
+ queryFn: () => sleep(10).then(() => 'data'),
+ })
+ await vi.advanceTimersByTimeAsync(10)
+
+ const data = await promise
+
+ expect(data).toBe('data')
+ expect(queryCache.find({ queryKey: key })?.state.data).toBe('data')
+
+ queryCache.clear()
+ })
+
+ it('should return existing data from the cache', async () => {
+ const key = queryKey()
+ const queryFn = vi.fn(() => sleep(10).then(() => 'data'))
+
+ function Page() {
+ const query = useQuery({ queryKey: key, queryFn })
+
+ const content = `status ${query.status}`
+
+ return (
+
+ )
+ }
+
+ queryClient.prefetchQuery({ queryKey: key, queryFn })
+ await vi.advanceTimersByTimeAsync(10)
+
+ const markup = renderToString(
+
+
+ ,
+ )
+
+ expect(markup).toContain('status success')
+ expect(queryFn).toHaveBeenCalledTimes(1)
+
+ queryCache.clear()
+ })
+
+ it('should add initialData to the cache', () => {
+ const key = queryKey()
+
+ function Page() {
+ const [page, setPage] = useState(1)
+ const { data } = useQuery({
+ queryKey: [key, page],
+ queryFn: () => sleep(10).then(() => page),
+ initialData: 1,
+ })
+
+ return (
+
+
{data}
+ setPage(page + 1)}>next
+
+ )
+ }
+
+ renderToString(
+
+
+ ,
+ )
+
+ const keys = queryCache.getAll().map((query) => query.queryKey)
+
+ expect(keys).toEqual([[key, 1]])
+
+ queryCache.clear()
+ })
+
+ it('useInfiniteQuery should return the correct state', async () => {
+ const key = queryKey()
+ const queryFn = vi.fn(() => sleep(10).then(() => 'page 1'))
+
+ function Page() {
+ const query = useInfiniteQuery({
+ queryKey: key,
+ queryFn,
+ getNextPageParam: () => undefined,
+ initialPageParam: 0,
+ })
+ return (
+
+ {query.data?.pages.map((page) => (
+ {page}
+ ))}
+
+ )
+ }
+
+ queryClient.prefetchInfiniteQuery({
+ queryKey: key,
+ queryFn,
+ initialPageParam: 0,
+ })
+ await vi.advanceTimersByTimeAsync(10)
+
+ const markup = renderToString(
+
+
+ ,
+ )
+
+ expect(markup).toContain('page 1')
+ expect(queryFn).toHaveBeenCalledTimes(1)
+
+ queryCache.clear()
+ })
+})
diff --git a/packages/preact-query/src/__tests__/suspense.test.tsx b/packages/preact-query/src/__tests__/suspense.test.tsx
new file mode 100644
index 0000000000..c363abb104
--- /dev/null
+++ b/packages/preact-query/src/__tests__/suspense.test.tsx
@@ -0,0 +1,186 @@
+import { queryKey, sleep } from '@tanstack/query-test-utils'
+import { render } from '@testing-library/preact'
+import type { ComponentChildren } from 'preact'
+import { Suspense } from 'preact/compat'
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
+
+import { QueryClient, QueryClientProvider, useSuspenseQuery } from '..'
+import type { QueryKey } from '..'
+
+function renderWithSuspense(client: QueryClient, ui: ComponentChildren) {
+ return render(
+
+ {ui}
+ ,
+ )
+}
+
+function createTestQuery(options: {
+ fetchCount: { count: number }
+ queryKey: QueryKey
+ staleTime?: number | (() => number)
+}) {
+ return function TestComponent() {
+ const { data } = useSuspenseQuery({
+ queryKey: options.queryKey,
+ queryFn: () =>
+ sleep(10).then(() => {
+ options.fetchCount.count++
+ return 'data'
+ }),
+ staleTime: options.staleTime,
+ })
+ return data: {data}
+ }
+}
+
+describe('Suspense Timer Tests', () => {
+ let queryClient: QueryClient
+ let fetchCount: { count: number }
+
+ beforeEach(() => {
+ vi.useFakeTimers()
+ queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ retry: false,
+ },
+ },
+ })
+ fetchCount = { count: 0 }
+ })
+
+ afterEach(() => {
+ vi.useRealTimers()
+ })
+
+ it('should enforce minimum staleTime of 1000ms when using suspense with number', async () => {
+ const TestComponent = createTestQuery({
+ fetchCount,
+ queryKey: ['test'],
+ staleTime: 10,
+ })
+
+ const rendered = renderWithSuspense(queryClient, )
+
+ expect(rendered.getByText('loading')).toBeInTheDocument()
+ await vi.advanceTimersByTimeAsync(10)
+ expect(rendered.getByText('data: data')).toBeInTheDocument()
+
+ rendered.rerender(
+
+
+
+
+ ,
+ )
+
+ await vi.advanceTimersByTimeAsync(10)
+
+ expect(fetchCount.count).toBe(1)
+ })
+
+ it('should enforce minimum staleTime of 1000ms when using suspense with function', async () => {
+ const TestComponent = createTestQuery({
+ fetchCount,
+ queryKey: ['test-func'],
+ staleTime: () => 10,
+ })
+
+ const rendered = renderWithSuspense(queryClient, )
+
+ expect(rendered.getByText('loading')).toBeInTheDocument()
+ await vi.advanceTimersByTimeAsync(10)
+ expect(rendered.getByText('data: data')).toBeInTheDocument()
+
+ rendered.rerender(
+
+
+
+
+ ,
+ )
+
+ await vi.advanceTimersByTimeAsync(10)
+
+ expect(fetchCount.count).toBe(1)
+ })
+
+ it('should respect staleTime when value is greater than 1000ms', async () => {
+ const TestComponent = createTestQuery({
+ fetchCount,
+ queryKey: queryKey(),
+ staleTime: 2000,
+ })
+
+ const rendered = renderWithSuspense(queryClient, )
+
+ expect(rendered.getByText('loading')).toBeInTheDocument()
+ await vi.advanceTimersByTimeAsync(10)
+ expect(rendered.getByText('data: data')).toBeInTheDocument()
+
+ rendered.rerender(
+
+
+
+
+ ,
+ )
+
+ await vi.advanceTimersByTimeAsync(1500)
+
+ expect(fetchCount.count).toBe(1)
+ })
+
+ it('should enforce minimum staleTime when undefined is provided', async () => {
+ const TestComponent = createTestQuery({
+ fetchCount,
+ queryKey: queryKey(),
+ staleTime: undefined,
+ })
+
+ const rendered = renderWithSuspense(queryClient, )
+
+ expect(rendered.getByText('loading')).toBeInTheDocument()
+ await vi.advanceTimersByTimeAsync(10)
+ expect(rendered.getByText('data: data')).toBeInTheDocument()
+
+ rendered.rerender(
+
+
+
+
+ ,
+ )
+
+ await vi.advanceTimersByTimeAsync(500)
+
+ expect(fetchCount.count).toBe(1)
+ })
+
+ it('should respect staleTime when function returns value greater than 1000ms', async () => {
+ const TestComponent = createTestQuery({
+ fetchCount,
+ queryKey: queryKey(),
+ staleTime: () => 3000,
+ })
+
+ const rendered = renderWithSuspense(queryClient, )
+
+ expect(rendered.getByText('loading')).toBeInTheDocument()
+ await vi.advanceTimersByTimeAsync(10)
+ expect(rendered.getByText('data: data')).toBeInTheDocument()
+
+ rendered.rerender(
+
+
+
+
+ ,
+ )
+
+ await vi.advanceTimersByTimeAsync(2000)
+
+ expect(fetchCount.count).toBe(1)
+ })
+})
diff --git a/packages/preact-query/src/__tests__/useInfiniteQuery.test-d.tsx b/packages/preact-query/src/__tests__/useInfiniteQuery.test-d.tsx
new file mode 100644
index 0000000000..ec858b1703
--- /dev/null
+++ b/packages/preact-query/src/__tests__/useInfiniteQuery.test-d.tsx
@@ -0,0 +1,143 @@
+import { QueryClient } from '@tanstack/query-core'
+import type { InfiniteData } from '@tanstack/query-core'
+import { describe, expectTypeOf, it } from 'vitest'
+
+import { useInfiniteQuery } from '../useInfiniteQuery'
+
+describe('pageParam', () => {
+ it('initialPageParam should define type of param passed to queryFunctionContext', () => {
+ useInfiniteQuery({
+ queryKey: ['key'],
+ queryFn: ({ pageParam }) => {
+ expectTypeOf(pageParam).toEqualTypeOf()
+ },
+ initialPageParam: 1,
+ getNextPageParam: () => undefined,
+ })
+ })
+
+ it('direction should be passed to queryFn of useInfiniteQuery', () => {
+ useInfiniteQuery({
+ queryKey: ['key'],
+ queryFn: ({ direction }) => {
+ expectTypeOf(direction).toEqualTypeOf<'forward' | 'backward'>()
+ },
+ initialPageParam: 1,
+ getNextPageParam: () => undefined,
+ })
+ })
+
+ it('initialPageParam should define type of param passed to queryFunctionContext for fetchInfiniteQuery', () => {
+ const queryClient = new QueryClient()
+ queryClient.fetchInfiniteQuery({
+ queryKey: ['key'],
+ queryFn: ({ pageParam }) => {
+ expectTypeOf(pageParam).toEqualTypeOf()
+ },
+ initialPageParam: 1,
+ })
+ })
+
+ it('initialPageParam should define type of param passed to queryFunctionContext for prefetchInfiniteQuery', () => {
+ const queryClient = new QueryClient()
+ queryClient.prefetchInfiniteQuery({
+ queryKey: ['key'],
+ queryFn: ({ pageParam }) => {
+ expectTypeOf(pageParam).toEqualTypeOf()
+ },
+ initialPageParam: 1,
+ })
+ })
+})
+describe('select', () => {
+ it('should still return paginated data if no select result', () => {
+ const infiniteQuery = useInfiniteQuery({
+ queryKey: ['key'],
+ queryFn: ({ pageParam }) => {
+ return pageParam * 5
+ },
+ initialPageParam: 1,
+ getNextPageParam: () => undefined,
+ })
+
+ // TODO: Order of generics prevents pageParams to be typed correctly. Using `unknown` for now
+ expectTypeOf(infiniteQuery.data).toEqualTypeOf<
+ InfiniteData | undefined
+ >()
+ })
+
+ it('should be able to transform data to arbitrary result', () => {
+ const infiniteQuery = useInfiniteQuery({
+ queryKey: ['key'],
+ queryFn: ({ pageParam }) => {
+ return pageParam * 5
+ },
+ initialPageParam: 1,
+ getNextPageParam: () => undefined,
+ select: (data) => {
+ expectTypeOf(data).toEqualTypeOf>()
+ return 'selected' as const
+ },
+ })
+
+ expectTypeOf(infiniteQuery.data).toEqualTypeOf<'selected' | undefined>()
+ })
+})
+describe('getNextPageParam / getPreviousPageParam', () => {
+ it('should get typed params', () => {
+ const infiniteQuery = useInfiniteQuery({
+ queryKey: ['key'],
+ queryFn: ({ pageParam }) => {
+ return String(pageParam)
+ },
+ initialPageParam: 1,
+ getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) => {
+ expectTypeOf(lastPage).toEqualTypeOf()
+ expectTypeOf(allPages).toEqualTypeOf>()
+ expectTypeOf(lastPageParam).toEqualTypeOf()
+ expectTypeOf(allPageParams).toEqualTypeOf>()
+ return undefined
+ },
+ getPreviousPageParam: (
+ firstPage,
+ allPages,
+ firstPageParam,
+ allPageParams,
+ ) => {
+ expectTypeOf(firstPage).toEqualTypeOf()
+ expectTypeOf(allPages).toEqualTypeOf>()
+ expectTypeOf(firstPageParam).toEqualTypeOf()
+ expectTypeOf(allPageParams).toEqualTypeOf>()
+ return undefined
+ },
+ })
+
+ // TODO: Order of generics prevents pageParams to be typed correctly. Using `unknown` for now
+ expectTypeOf(infiniteQuery.data).toEqualTypeOf<
+ InfiniteData | undefined
+ >()
+ })
+})
+
+describe('error booleans', () => {
+ it('should not be permanently `false`', () => {
+ const {
+ isFetchNextPageError,
+ isFetchPreviousPageError,
+ isLoadingError,
+ isRefetchError,
+ } = useInfiniteQuery({
+ queryKey: ['key'],
+ queryFn: ({ pageParam }) => {
+ return pageParam * 5
+ },
+ initialPageParam: 1,
+ getNextPageParam: () => undefined,
+ })
+
+ expectTypeOf(isFetchNextPageError).toEqualTypeOf