React Query
React Query users
| # | Actions | Status | ||
|---|---|---|---|---|
| No data available. | ||||
List, create, edit, and delete all run through a real QueryClient (caching + automatic invalidation on mutation) — only the REST call underneath is swapped for an in-memory mock since this docs site has no backend.
@vibeflui/react-query registers a TanStack Query-compatible data provider. It does not render fields or table cells — it changes how list/detail/create/update/delete/action operations are executed, adding caching and automatic invalidation on top of the base provider (REST by default).
Install:
npm install @vibeflui/react-query @tanstack/react-queryFile structure
schemas/
users/
react-query-users.schema.ts
lib/
vibeflui/
registry.tsx
app/
users/
page.tsxSchema file
schemas/users/react-query-users.schema.ts
export const reactQueryUsersSchema = {
resource: "users",
mode: "server",
identity: { key: "id", param: "id" },
endpoint: {
list: "/api/users",
create: { url: "/api/users", method: "POST" },
update: { url: "/api/users/:id", method: "PATCH" },
delete: { url: "/api/users/:id", method: "DELETE" }
},
provider: {
type: "custom",
name: "reactQuery"
},
table: {
dataPath: "data",
totalPath: "total",
columns: [
{ key: "name", label: "Name", searchable: true, sortable: true },
{ key: "email", label: "Email", searchable: true },
{ key: "status", label: "Status" }
],
search: { enabled: true, param: "search" },
pagination: { enabled: true, pageSize: 10, pageParam: "page", pageSizeParam: "pageSize" },
sort: { enabled: true, defaultKey: "name", defaultDirection: "asc" }
},
form: {
mode: "modal",
fields: [
{ name: "name", label: "Name", required: true },
{ name: "email", label: "Email", type: "email", required: true },
{
name: "status",
type: "select",
options: [
{ label: "Active", value: "active" },
{ label: "Pending", value: "pending" }
]
}
]
},
actions: {
create: { enabled: true, label: "Create user" },
edit: { enabled: true },
delete: { enabled: true, confirm: true, variant: "danger" }
}
} as const;provider.name: "reactQuery" must match the plugin's providerName (defaults to "reactQuery"). endpoint is still needed — the plugin's provider calls the base REST provider underneath, it just wraps it with query caching and invalidation.
Registry file
lib/vibeflui/registry.tsx
import { QueryClient } from "@tanstack/react-query";
import { createReactQueryPlugin } from "@vibeflui/react-query";
export const queryClient = new QueryClient();
export const reactQueryPlugin = createReactQueryPlugin({ queryClient });Page file
app/users/page.tsx
"use client";
import { QueryClientProvider } from "@tanstack/react-query";
import { FluiKit, FluiKitProvider } from "@vibeflui/core";
import { queryClient, reactQueryPlugin } from "@/lib/vibeflui/registry";
import { reactQueryUsersSchema } from "@/schemas/users/react-query-users.schema";
export default function UsersPage() {
return (
<QueryClientProvider client={queryClient}>
<FluiKitProvider plugins={[reactQueryPlugin]}>
<FluiKit schema={reactQueryUsersSchema} />
</FluiKitProvider>
</QueryClientProvider>
);
}The page needs both QueryClientProvider (from @tanstack/react-query, so the rest of the app can also use useQuery/useMutation) and FluiKitProvider with the plugin registered.
Query behavior
Read operations use ensureQueryData when available, then fetchQuery, then the base provider directly. Mutation operations run through the base provider and invalidate queries afterward unless invalidateOnMutation is false.
export const reactQueryPlugin = createReactQueryPlugin({
queryClient,
providerName: "adminQuery",
invalidateOnMutation: true,
refetchOnMutation: true,
queryKey: (context, operation) => ["admin", context.schema.resource, operation, context.params]
});baseProvider can also be overridden — pass any FluiKitDataProvider (not just the default REST one) to wrap it with React Query's caching, e.g. a custom or in-memory provider.
Exports include createReactQueryPlugin, ReactQueryPlugin, createReactQueryProvider, defaultReactQueryKey, and defaultReactQueryInvalidationKey.