Add pagination to discord bot using buttons or menu.
Version 16.6.0 or newer of Node.js is required
npm install @discordx/pagination
yarn add @discordx/pagination
embeds: (string | MessageEmbed | MessageOptions)[] | Pagination
)import { Pagination, PaginationResolver } from "@discordx/pagination";
import type {
CommandInteraction,
MessageActionRowComponentBuilder,
} from "discord.js";
import {
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
EmbedBuilder,
} from "discord.js";
import type { ArgsOf } from "discordx";
import { Discord, On, Slash } from "discordx";
function GeneratePages(limit?: number): MessageOptions[] {
const pages = Array.from(Array(limit ?? 20).keys()).map((i) => {
return { content: `I am ${i + 1}`, embed: `Demo ${i + 1}` };
});
return pages.map((page) => {
return {
content: page.content,
embeds: [new MessageEmbed().setTitle(page.embed)],
};
});
}
@Discord()
export class Example {
// example: message
@On({ event: "messageCreate" })
async messageCreate([message]: ArgsOf<"messageCreate">): Promise<void> {
if (message.content === "paginated demo") {
const pagination = new Pagination(message, GeneratePages());
await pagination.send();
}
}
// example: any text channel
@On({ event: "messageCreate" })
async messageCreateChannel([
message,
]: ArgsOf<"messageCreate">): Promise<void> {
if (message.content === "paginated channel demo") {
const pagination = new Pagination(message.channel, GeneratePages());
await pagination.send();
}
}
// example: simple slash with button pagination
@Slash({ description: "Simple slash with button pagination", name: "demo-a" })
async demoA(interaction: CommandInteraction): Promise<void> {
const embedX = new PaginationResolver((page, pagination) => {
if (page === 3) {
// example to replace pagination with another pagination data
pagination.currentPage = 0; // reset current page, because this is gonna be first page
pagination.maxLength = 5; // new max length for new pagination
pagination.pages = [
{ content: "1" },
{ content: "2" },
{ content: "3" },
{ content: "4" },
{ content: "5" },
]; // page reference can be resolver as well
return (
pagination.pages[pagination.currentPage] ?? { content: "unknown" }
); // the first page, must select ourselves
}
return { content: `page v2 ${page}` };
}, 25);
const pagination = new Pagination(interaction, embedX, {
onTimeout: () => {
void interaction.deleteReply().catch(null);
},
buttons: {
backward: {
emoji: { name: "🙂" },
},
},
time: 60_000,
enableExit: true,
});
await pagination.send();
}
// example: simple slash with menu pagination
@Slash({ description: "Simple slash with menu pagination", name: "demo-b" })
async demoB(interaction: CommandInteraction): Promise<void> {
const pagination = new Pagination(interaction, GeneratePages(), {
time: 60_000,
});
await pagination.send();
}
// example: simple string array
@Slash({ description: "Simple string array", name: "demo-c" })
async demoC(interaction: CommandInteraction): Promise<void> {
const pagination = new Pagination(
interaction,
Array.from(Array(200).keys()).map((i) => ({
content: (i + 1).toString(),
})),
{
enableExit: true,
},
);
await pagination.send();
}
// example: array of custom message options
@Slash({ description: "Array of custom message options", name: "demo-d" })
async demoD(interaction: CommandInteraction): Promise<void> {
const pagination = new Pagination(interaction, [
{
content: "Page 1",
},
{
content: "Page 2",
embeds: [new EmbedBuilder({ title: "It's me embed 2" })],
},
{
components: [
new ActionRowBuilder<MessageActionRowComponentBuilder>().addComponents(
[
new ButtonBuilder({
customId: "myCustomId",
label: "My Custom Button",
style: ButtonStyle.Primary,
}),
],
),
],
content: "Page 3",
embeds: [new EmbedBuilder({ title: "It's me embed 3" })],
},
]);
await pagination.send();
}
}
Name | Type | Default | Description |
---|---|---|---|
debug | boolean | false | Enable debug logging |
enableExit | boolean | false | Enable exit button for early pagination closure |
ephemeral | boolean | undefined | Set ephemeral response |
initialPage | number | 0 | Initial page number |
itemsPerPage | number | undefined | Number of items shown per page in select menu |
onTimeout | Function | undefined | Callback function when pagination times out |
The following options are available under the buttons
configuration:
Name | Type | Description |
---|---|---|
previous | ButtonOptions | Previous button configuration |
backward | ButtonOptions | Backward button configuration (-10 pages) |
forward | ButtonOptions | Forward button configuration (+10 pages) |
next | ButtonOptions | Next button configuration |
exit | ButtonOptions | Exit button configuration |
skipAmount | number | Number of pages to skip with skip buttons (default: 10) |
Name | Type | Description |
---|---|---|
emoji | ComponentEmojiResolvable | null | Button emoji |
id | string | Custom button ID |
label | string | Button label text |
style | ButtonStyle | Button style (PRIMARY|SECONDARY|SUCCESS|DANGER) |
The following options are available under the selectMenu
configuration:
Name | Type | Default | Description |
---|---|---|---|
labels.start | string | "Start" | Start label text |
labels.end | string | "End" | End label text |
menuId | string | "discordx@pagination@menu" | Custom select menu ID |
pageText | string | string[] | "Page {page} " |
Page text format (use {page} for page number) |
rangePlaceholderFormat | string | undefined | Custom range placeholder format (use {start} , {end} , {total} ) |