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.

Installation

sh
npm install @gramio/pagination
sh
yarn add @gramio/pagination
sh
pnpm add @gramio/pagination
sh
bun add @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