Question
QTable @request Pagination: Choosing Offset vs Cursor for Large Datasets
Nia Orbit
0 reputation · 19 Mar 2026, 21:48 UTC
103.1K views
0 reputation · 19 Mar 2026, 21:48 UTC
Implement server‑side pagination for a QTable that displays millions of rows. The table must support dynamic sorting and filtering while keeping the UI responsive.
@request event delivers a pagination object with page, rowsPerPage, sortBy, descending, and filter fields.rowsNumber to calculate the number of pages.How to reconcile cursor‑based pagination, which is more efficient and stable under concurrent modifications, with Quasar’s page‑number UI that expects offset‑style navigation?
rowsNumber required by QTable without re‑implementing the entire pager?QTable pagination component to display a “Next”/“Previous” style UI that works with cursor tokens?@request handler translate the pagination payload into a cursor‑based query, and what fallback exists if the cursor is invalid?rowsNumber for a cursor backend
/count endpoint that returns an approximate or exact total.
GET /api/items/count
{ "total": 12456789 }
COUNT(*) can be expensive. Use a dedicated counter table or a materialized view that is updated on inserts/deletes. The counter can be refreshed asynchronously.
rowsNumber in the same response that contains the first page. Subsequent pages can reuse the same value; if the count changes, the pager will automatically recalc.
QPagination slot with a small component that only shows Next and Previous buttons.
<template #pagination>
<div class="flex items-center">
<q-btn flat icon="chevron_left" @click=>"prev()" :disabled=>!hasPrev>
<span class="mx-2">Page {{ page }} of {{ totalPages }}</span>
<q-btn flat icon="chevron_right" @click=>"next()" :disabled=>!hasNext>
</div>
</template>
pagination object’s page and rowsPerPage fields so that Quasar’s reactivity still works. Store the cursor token in a hidden field (e.g., pagination.cursor) that is passed back to the server.
@request.
@request payloads to cursor queries
pagination.page === 1, the handler should ignore pagination.cursor and issue a query that starts from the first record according to the current sort/filter.
pagination.cursor (a base64‑encoded composite key of the last row from the previous page). It uses that as the WHERE condition for the next set of rows:
SELECT * FROM items
WHERE (sortKey, id) >= :cursor
ORDER BY sortKey ASC, id ASC
LIMIT :rowsPerPage;
SELECT * FROM items
ORDER BY sortKey ASC, id ASC
LIMIT :rowsPerPage OFFSET :((page-1)*rowsPerPage);
{
rows: [...],
rowsNumber: total,
cursor: encodeCursor(lastRow)
}
pagination.cursor before emitting @request again. This keeps the client stateless beyond the hidden field.
rowsNumber and a cursor token for page 1.pagination.cursor and fetches the next slice.QTable slots.count endpoint or maintain a counter table.updated_at) can invalidate them.Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.