TanStack Query Integration

Direct Query Support

UI States has built-in support for TanStack Query. Simply pass the query result directly:

typescript
import { UIStates } from "@promise-inc/ui-states";
import { useQuery } from "@tanstack/react-query";
function UserProfile() {
const query = useQuery({
queryKey: ["user"],
queryFn: () => fetch("/api/user").then(res => res.json()),
});
return (
<UIStates query={query}>
<div>
<h1>{query.data.name}</h1>
<p>{query.data.email}</p>
</div>
</UIStates>
);
}

Automatic State Detection

When you pass a query object, UI States automatically extracts:

  • data from query.data
  • loading from query.isLoading
  • error from query.error

Refetch on Error

Error states automatically include a retry button that calls query.refetch():

typescript
const query = useQuery({
queryKey: ["user"],
queryFn: fetchUser,
retry: 3, // TanStack Query retry
});
<UIStates query={query}>
<div>{query.data.name}</div>
</UIStates>
// Error state shows "Retry" button that calls query.refetch()

Query with Caching

Combine TanStack Query with skeleton caching for optimal UX:

typescript
function UserList() {
const query = useQuery({
queryKey: ["users"],
queryFn: fetchUsers,
});
return (
<UIStates
query={query}
enableCache={true}
cacheKey="user-list"
>
{query.data.map(user => (
<div key={user.id}>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
))}
</UIStates>
);
}

Manual Props with Query

You can override automatic detection by passing manual props:

typescript
<UIStates
query={query}
loading={query.isLoading || query.isFetching} // Include background refetch
data={query.data?.items} // Use nested data
>
{query.data.items.map(item => (
<div key={item.id}>{item.name}</div>
))}
</UIStates>
The query prop accepts any object with data, isLoading, and error properties, so it works with other data fetching libraries that follow a similar pattern.