---
title: Manual Pagination - Fixed Pages
slug: experience/manual-pagination-fixed-pages
docTags: 
createdAt: 2026-10-06T13:20:24.667Z
---

## Goal

- Display a fixed number of items (e.g. 10)
- No automatic loading on scroll
- Navigation only via **Next** and **Previous** buttons, which replace the currently visible items with the next/previous page's items

## Concept

Pagination is controlled using:

- A **limit** on the datasource
- A **cursor/offset** stored in context
- Buttons that modify the offset via navigation params

## Layout warning

Because the list re-renders with a new set of items on every navigation, the page layout can shift (the new page may be taller or shorter than the previous one). Give the list container a **fixed height** (or another layout technique that reserves space) so the surrounding page does not jump when the user navigates between pages.

## Required components

To implement manual pagination, configure **3 components**:

1. List Component
2. Previous Button
3. Next Button

## 1. List Component Configuration

### Step 1 - Enable Data Source

In **List Component -> Datasource**:

- Enable datasource
- Choose the datasource type as needed

### Step 2 - Limit (Page Size)

Set:

- **Limit = 10** (or any page size you want)

This ensures only 10 elements are shown, and infinite scrolling is disabled.

### Step 3 - Cursor / Offset

Add a **Cursor field**:

- **Value:** `$context.offset`
- **Condition:** `$context.offset IS SET`

This tells the data source from which position it should load data.

You may name the parameter something else (e.g. `pageOffset`), but then all references must use `$context.pageOffset`.

```json
{
  "content": {
    "tag": "div",
    "type": "html",
    "content": "$context.content.name"
  },
  "dataSource": {
    "type": "content",
    "limit": 10,
    "fetchOptions": {
      "totalCount": true
    },
    "contextKey": "contents",
    "cursor": {
      "value": "$context.offset",
      "condition": {
        "value": "$context.offset",
        "operation": "SET"
      }
    }
  },
  "type": "list"
}
```

## 2. "Previous" Button Configuration

### Visibility condition

Hide the button conditionally rather than disabling it, so it is simply not in the DOM on the first page:

```javascript
$context.offset > 0
```

### Tap action

Set **Tap Action -> Navigation**:

- Path: current path
- Params:
  - Key: `offset`
  - Value: `$functions.add($context.offset, -10)`

This decreases the offset by the page size and loads the previous set of elements.

```json
{
  "tag": "div",
  "type": "html",
  "content": "prev page",
  "tap": {
    "type": "navigate",
    "params": {
      "offset": "$functions.add($context.offset, -10)"
    }
  },
  "condition": {
    "compareValue": "$functions.add($context.offset, 0)",
    "value": "0",
    "negated": true
  }
}
```

## 3. Next Button Configuration

### Visibility condition

Hide the button conditionally when there is no further page:

```javascript
$context.contents.hasNextPage == true
```

Where `contents` is the datasource result context key, and `hasNextPage` indicates more data is available.

### Tap action

Set **Tap Action -> Navigation**:

- Path: current path
- Params:
  - Key: `offset`
  - Value: `$functions.add($context.offset, 10)`

This increases the offset by the page size and loads the next set of elements.

```json
{
  "tag": "div",
  "type": "html",
  "content": "next page",
  "tap": {
    "type": "navigate",
    "params": {
      "offset": "$functions.add($context.offset, 10)"
    }
  },
  "condition": {
    "operation": "EQUALS",
    "compareValue": "true",
    "value": "$context.contents.hasNextPage"
  }
}
```

## Custom function

Add the **add** function to `custom.server.js`:

```javascript
window.$functions = {
    add: (val, plus) => parseInt(val ?? '0') + plus
}
```

Note: an earlier draft of this page also defined a `toJson` helper for debugging context values during setup. Remove it before publishing - it has no purpose in a finished configuration.

## Summary

| Feature                   | Infinite Scrolling | Manual Pagination - Fixed Pages |
| ------------------------- | ------------------ | ------------------------------- |
| Auto-load on scroll       | Yes                | No                              |
| Uses limit                | No                 | Yes                             |
| Uses offset/cursor        | No                 | Yes                             |
| Requires buttons          | No                 | Yes - Next / Prev               |
| Replaces or appends items | -                  | Replaces                        |
| Page size control         | Batch Size         | Limit                           |

