Skip to content

Pagination

npmnpm downloadsJSRJSR Score

A fluent, chainable API for creating paginated inline keyboard menus in Telegram bots. Handles data fetching, keyboard generation, navigation buttons, page info, and item selection callbacks.

WARNING

This package is in WIP stage — the API may change.

Installation

bash
npm install @gramio/pagination
bash
yarn add @gramio/pagination
bash
pnpm add @gramio/pagination
bash
bun install @gramio/pagination

IMPORTANT

You must override @gramio/callback-data version in your package.json:

json
{
    "overrides": {
        "@gramio/callback-data": "^0.0.11"
    }
}

Usage

ts
import { Bot } from "gramio";
import { Pagination } from "@gramio/pagination";
import { paginationFor } from "@gramio/pagination/plugin";

const data = [
    { id: 1, title: "test" },
    { id: 2, title: "test2" },
    { id: 3, title: "test3" },
    { id: 4, title: "test4" },
    { id: 5, title: "test5" },
];

const paginationTest = new Pagination("test", async ({ offset, limit }) => {
    return data.slice(offset, offset + limit);
})
    .count(() => Promise.resolve(data.length))
    .item((x) => ({
        title: x.title,
        id: x.id,
    }))
    .onSelect(({ id, context }) => {
        return context.editText(`Selected ${id}`, {
            reply_markup: context.message?.replyMarkup?.payload,
        });
    })
    .limit(2)
    .columns(2)
    .withFirstLastPage()
    .withPageInfo(
        ({ totalPages, currentPage }) => `${currentPage} / ${totalPages}`
    );

const bot = new Bot(process.env.BOT_TOKEN as string)
    .extend(paginationFor([paginationTest]))
    .command("start", async (ctx) =>
        ctx.reply("Hello", {
            reply_markup: await paginationTest.getKeyboard(0),
        })
    )
    .onStart(console.log);

await bot.start();

API

new Pagination(name, dataFunction)

Create a pagination instance.

  • name — unique identifier (used as callback data prefix)
  • dataFunctionasync ({ offset, limit }) => Data[] — fetches one page of items

Chainable methods

MethodDescription
.limit(count)Items per page (default: 10)
.columns(count)Button columns in the keyboard grid
.count(func)async () => number — total item count for full page info
.item(func)(data) => { title, id } — map each item to a button
.onSelect(callback)({ id, context }) => void — handler for item button taps
.withPageInfo(format)({ totalPages, currentPage }) => string — page info button text
.withFirstLastPage()Add ⏮️/⏭️ first/last page buttons
.wrapKeyboard(func)Post-process the keyboard to add extra buttons
.selectCallbackData(func)Override callback data for selection buttons

Getting the keyboard

ts
// Get just the keyboard
const keyboard = await pagination.getKeyboard(offset);

// Get keyboard + fetched data + pagination info
const { keyboard, data, pagination: info } = await pagination.getKeyboardWithData(offset);

// Get data + pagination info without keyboard
const { data, pagination: info } = await pagination.getDataWithPaginationInfo(offset);

paginationFor(paginationList)

GramIO plugin that routes callback_query events to the correct Pagination instance.

ts
import { paginationFor } from "@gramio/pagination/plugin";

const bot = new Bot("")
    .extend(paginationFor([pagination1, pagination2]));

Pagination strategies

Without .count() (limit+1)

Fetches limit + 1 items to detect if there's a next page. No total pages or current page info.

With .count()

Runs count and data queries in parallel. Enables .withPageInfo() and .withFirstLastPage() features.

ButtonCondition
⏮️ First page.withFirstLastPage() + has previous
⬅️ Previoushas previous page
Page info.withPageInfo() + .count() set
➡️ Nexthas next page
⏭️ Last page.withFirstLastPage() + has next