TanStack Query Integration for PowerSync
@powersync/tanstack-react-query
provides seamless integration between PowerSync's Web SDK and TanStack Query for React. It wraps TanStack's useQuery
and useSuspenseQuery
hooks to work easily with PowerSync's SQL queries. This combines PowerSync's existing watched queries with TanStack Query's features like the paginated queries, caching, and Suspense.
Note: Alpha Release
This package is currently in an alpha release.
Getting Started
To use @powersync/tanstack-react-query
, you need to set up both the PowerSyncContext
and the TanStack QueryClientProvider
in your application.
// App.jsx
import { PowerSyncDatabase } from '@powersync/web';
// or for React Native
// import { PowerSyncDatabase } from '@powersync/react-native';
import { PowerSyncContext } from "@powersync/react";
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
export const App = () => {
const powerSync = React.useMemo(() => {
// Set up PowerSync client
}, [])
const queryClient = React.useMemo(() => new QueryClient(), [])
return (
<PowerSyncContext.Provider value={powerSync}>
<QueryClientProvider client={queryClient}>
{/** Your components go here */}
</QueryClientProvider>
</PowerSyncContext.Provider>
);
};
Usage
useQuery
The useQuery
hook from @powersync/tanstack-react-query
works similarly to the standard TanStack React Query useQuery
hook but integrates with PowerSync for watched query functionality. Queries automatically update when dependent tables change.
// TodoListDisplay.jsx
import { useQuery } from '@powersync/tanstack-react-query';
export const TodoListDisplay = () => {
const { data: todoLists, isLoading, isFetching, error } = useQuery({
queryKey: ['todoLists'],
query: 'SELECT * FROM lists WHERE id = ?', // use `query` instead of `queryFn` to define a SQL query - this allows watching underlying tables for changes
parameters: ['id-1'], // supply query parameters for the SQL query
});
if (isLoading) {
return <div>Loading todo lists...</div>;
}
if (error) {
return <div>Error loading todo lists: {error.message}</div>;
}
return (
<ul>
{todoLists?.map((list) => (
<li key={list.id}>{list.name}</li>
))}
</ul>
);
};
useSuspenseQuery
If you prefer to use React’s Suspense
for data fetching, you can use the useSuspenseQuery
hook.
// TodoListDisplaySuspense.jsx
import { Suspense } from 'react';
import { useSuspenseQuery } from '@powersync/tanstack-react-query';
const TodoListContent = () => {
const { data: todoLists } = useSuspenseQuery({
queryKey: ['todoLists'],
query: 'SELECT * FROM lists',
});
return (
<ul>
{todoLists.map((list) => (
<li key={list.id}>{list.name}</li>
))}
</ul>
);
};
export const TodoListDisplaySuspense = () => {
return (
/* Will show fallback while query is loading */
<Suspense fallback={<div>Loading todo lists...</div>}>
<TodoListContent />
</Suspense>
);
};
TypeScript Support
A type can be specified for each row returned by useQuery
and useSuspenseQuery
.
// TodoListDisplay.tsx
const TodoListContent = () => {
const { data } = useQuery<{ id: string, name: string }>({
queryKey: ['todoLists'],
query: 'SELECT * FROM lists',
});
const { data: todoListsSuspense } = useSuspenseQuery<{ id: string, name: string }>({
queryKey: ['todoListsSuspense'],
query: 'SELECT * FROM lists',
});
return <></>
}
Kysely Support
You are also able to use a compilable query (e.g. Kysely queries) as a query argument in place of a SQL statement string.
// TodoListDisplay.tsx
import { useQuery } from '@powersync/tanstack-react-query';
export const TodoListDisplay = () => {
const { data: todoLists, isLoading, error } = useQuery({
queryKey: ['todoLists'],
query: kyselyDb.selectFrom('lists').selectAll(), // The type of the rows in `data` are inferred from the Kysely query
});
if (isLoading) {
return <div>Loading todo lists...</div>;
}
if (error) {
return <div>Error loading todo lists: {error.message}</div>;
}
return (
<ul>
{todoLists?.map((list) => (
<li key={list.id}>{list.name}</li>
))}
</ul>
);
};