Paginate a List
When a dataset is too large to display all at once, pagination lets users move through it in fixed-size pages. XMLUI has two paths depending on which component you're working with.
If you're using a Table, pagination is built in — just set isPaginated and the component handles page state for you:
<Table
data="/api/endpoint"
isPaginated
pageSize="10"
pageSizeOptions="{[5, 10, 20, 30]}"
paginationControlsLocation="both"
>
...
</Table>For components like List that don't have pagination built in, wire a DataSource and a Pagination component together through reactive variables. The Pagination component reports page changes; you update the offset variables; DataSource re-fetches automatically with the new query parameters.
<App
var.pageSize="{5}"
var.currentPage="{0}"
var.before="{0}"
var.after="{pageSize-1}"
>
<DataSource
id="pagination_ds"
url="/api/pagination_items"
queryParams="{{ from: before, to: after }}"
/>
<Pagination
itemCount="20"
pageSize="{pageSize}"
pageIndex="{currentPage}"
onPageDidChange="(page, size, total) => {
currentPage = page;
before = page * size;
after = before + size - 1;
}"
/>
<List data="{pagination_ds}" />
</App><App
var.pageSize="{5}"
var.currentPage="{0}"
var.before="{0}"
var.after="{pageSize-1}"
>
<DataSource
id="pagination_ds"
url="/api/pagination_items"
queryParams="{{ from: before, to: after }}"
/>
<Pagination
itemCount="20"
pageSize="{pageSize}"
pageIndex="{currentPage}"
onPageDidChange="(page, size, total) => {
currentPage = page;
before = page * size;
after = before + size - 1;
}"
/>
<List data="{pagination_ds}" />
</App>Key points
Table has pagination built in — List needs it wired manually: Set isPaginated on Table and it handles page state internally. For List (or any component without built-in pagination), combine a DataSource with a Pagination component and wire them together through reactive variables.
Pagination tells you the new page; you update the variables: The onPageDidChange handler receives (page, size, total). Assign the new page index to currentPage and recalculate before and after offsets — both reactive variables are read by DataSource's queryParams, so the data refetch happens automatically.
queryParams on DataSource drives the API slice: Pass the offset or cursor values as query parameters using queryParams="{{ from: before, to: after }}". The server-side handler uses these to return only the requested page of data. The API shape (offset/limit, cursor, page number) depends on your backend — adjust the variable names and arithmetic accordingly.
itemCount on Pagination must match the total record count from your API: The component uses this number to calculate how many page buttons to show. If your API returns the total count in the response, read it with resultSelector or a separate DataSource and bind it to itemCount.
pageSize controls both the visual controls and the slice math: Declare it as a reactive variable (var.pageSize="{5}") so that if the user changes the page size through Pagination's size selector, the same value flows into the offset recalculation in onPageDidChange and into the DataSource query automatically.
See also
- Render a flat list with custom cards — customize how each row in the paginated list is displayed
- Group items in List by a property — combine grouping with a paginated data source
- Poll an API at regular intervals — keep the current page fresh with periodic refetches