#preloadQuery and pagination query
10 messages · Page 1 of 1 (latest)
Hi @jagged bough. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
Based on the knowledge sources, there's currently a limitation with preloading paginated queries in Next.js with Convex. According to a Discord support thread, you cannot directly preload a paginated query because of how pagination cursors work in Convex.
The issue is that pagination cursors in Convex are session-specific. When you preload data on the server, the request is sent by the Convex client on the server (e.g., Vercel's servers). When you try to hydrate that data on the client, the client attempts to use the same cursor with a different session ID, resulting in an error like "Failed to parse cursor" Discord support thread.
The recommended workaround is to:
- Create a regular, non-paginated query that fetches the same initial chunk of data you would want to display first
- Use this non-paginated query with
preloadQueryfor server rendering - Then use the paginated query on the client side after hydration
This approach means maintaining two separate queries that fetch similar data, which is admittedly inconvenient but currently necessary.
A community member in the Discord thread mentioned:
"Server side rendering a page that uses a paginated query is possible, convex just doesn't give you the sugar. [...] Try a nice skeleton loader instead, they work wonders."
The Convex team has indicated that TanStack-style SSR is more aligned with their approach rather than traditional Next.js server rendering for paginated queries.
If you're looking for alternatives, you might consider using skeleton loaders while the paginated data loads on the client side, which can provide a good user experience without the complexity of trying to preload paginated queries.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@jagged bough Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@ivory robin What about using query params, useOptimistic and useTransition for filtering a query?
Let me check the knowledge base for you again. One moment! :books:
I understand you're asking about using query parameters, optimistic updates, and transitions for filtering queries in Convex. Let me address these aspects based on the knowledge sources.
Query Parameters and Dynamic Filtering
Convex supports building dynamic queries where you can conditionally apply filters based on query parameters. However, there are some TypeScript challenges when building these queries dynamically.
The traditional approach of building a query with conditional filters like this:
let query = ctx.db.query("messages");
if (args.authorFilter !== undefined) {
query = query.withIndex("by_author", q=>q.eq("author", args.authorFilter));
}
// more conditional filters...
doesn't work well with TypeScript because the query's type changes after each operation Stack: Dynamic Query Builders.
Instead, you can use the filter helper from convex-helpers/server/filter which provides more powerful filtering capabilities:
import { filter } from "convex-helpers/server/filter";
filter(ctx.db.query("posts"),
(post) => ...unlimited functionality...
).paginate(opts);
This approach allows for more complex filtering logic while maintaining type safety Stack: Complex Filters in Convex.
Optimistic Updates
For optimistic updates, Convex provides the .withOptimisticUpdate method that you can use with mutations to immediately update the UI before the server confirms the change:
const createNote = useMutation(api.notes.createNote).withOptimisticUpdate(
(localStore, args) => {
const existingNotes =
localStore.getQuery(api.notes.getNotes, { boardId: actualBoardId }) || [];
const tempId = `temp_${Date.now()}` as Id<"notes">;
const now = Date.now();
localStore.setQuery(api.notes.getNotes, { boardId: actualBoardId }, [
...existingNotes,
{ _id: tempId, _creationTime: now, ...args },
]);
}
);
This helps create a more responsive user experience by showing changes immediately while the actual mutation is processing Stack: Keeping Users in Sync.
The OptimisticLocalStore interface provides methods like getQuery and getAllQueries to access the current state of queries in the client, which you can use to update the UI optimistically Convex Docs: OptimisticLocalStore.
Pagination with Filters
For pagination with filters, you can use the pagination feature with the filter helper:
export const postsWithTag = query({
args: { tag: v.string(), paginationOpts: paginationOptsValidator },
handler: (ctx, args) => {
return filter(
ctx.db.query("posts"),
(post) => post.tags.includes(args.tag),
).paginate(args.paginationOpts);
},
});
This approach allows you to apply complex filters while still supporting pagination Stack: Complex Filters in Convex.
While the knowledge sources don't specifically mention useTransition (which is a React feature), the combination of Convex's reactive queries and optimistic updates should provide a smooth user experience when filtering data.