Inline and Custom Keyboards (built-in)

Your bot may send a number of buttons, either to be displayed underneath a message, or to replace the user’s keyboard. They are called inline keyboards and custom keyboards, respectively. If you think that this is confusing, then that’s because it is. Thank you, Telegram, for this overlapping terminology.

Let us try to clear it up a bit:

TermDefinition
Inline Keyboarda set of buttons that is displayed underneath a message inside the chat
Custom Keyboarda set of buttons that is displayed instead of the user’s system keyboard
Inline Keyboard buttona button in an inline keyboard, sends a callback query not visible to the user when pressed, sometimes just called inline button
Custom Keyboard buttona button in a keyboard, sends a text message with its label when pressed, sometimes just called keyboard button
InlineKeyboardclass in grammY to create inline keyboards
Keyboard (!)class in grammY to create custom keyboards

Note that both custom keyboard buttons and inline keyboard buttons can also have other functions, such as requesting the user’s location, opening a website, and so on. This was omitted for brevity.

It is not possible to specify both a custom keyboard and an inline keyboard in the same message. The two are mutually exclusive. Moreover, the sent kind of reply markup cannot be changed at a later point by editing the message. For example, it is not possible to first send a custom keyboard along with a message, and then edit the message to use an inline keyboard.

Inline Keyboards

Revisit the inline keyboard section in the Introduction for Developersopen in new window written by the Telegram team.

grammY has a simple and intuitive way to build up the inline keyboards that your bot can send along with a message. It provides a class called InlineKeyboard for this.

Both switchInline and switchInlineCurrent buttons start inline queries. Check out the section about Inline Queries for more information about how they work.

Building an Inline Keyboard

Here are three examples how to build an inline keyboard with text buttons.

You can also use other methods like url to let the Telegram clients open a URL, and many more options as listed in the grammY API Referenceopen in new window as well as the Telegram Bot API Referenceopen in new window for InlineKeyboard.

Example 1

Buttons for a pagination navigation can be built like this:

Code
const inlineKeyboard = new InlineKeyboard()
  .text("« 1", "first")
  .text("‹ 3", "prev")
  .text("· 4 ·", "stay")
  .text("5 ›", "next")
  .text("31 »", "last");
Result

Example 1

Example 2

An inline keyboard with share button can be built like this:

Code
const inlineKeyboard = new InlineKeyboard()
  .text("Get random music", "random").row()
  .switchInline("Send music to friends");
Result

Example 2

Example 3

URL buttons can be built like this:

Code
const inlineKeyboard = new InlineKeyboard().url(
  "Read on TechCrunch",
  "https://techcrunch.com/2016/04/11/this-is-the-htc-10/",
);
Result

Example 3

Sending an Inline Keyboard

You can send an inline keyboard directly along a message, no matter whether you use bot.api.sendMessage, ctx.api.sendMessage, or ctx.reply:

// Send inline keyboard with message.
await ctx.reply(text, {
  reply_markup: inlineKeyboard,
});

Naturally, all other methods that send messages other than text messages support the same options, as specified by the Telegram Bot API Referenceopen in new window. For example, you can edit a keyboard by calling editMessageReplyMarkup, and passing the new InlineKeyboard instance as reply_markup. Specify an empty inline keyboard to remove all buttons underneath a message.

Responding to Clicks

Menu Plugin

The keyboard plugin gives you raw access to the update objects that Telegram sends. However, responding to clicks this way can be tedious. If you are looking for a more high-level implementation of inline keyboards, check out the menu plugin. It makes it simple to create interactive menus.

Every text button has a string as callback data attached. If you don’t attach callback data, grammY will use the button’s text as data.

Once a user clicks a text button, your bot will receive an update containing the corresponding button’s callback data. You can listen for callback data via bot.callbackQuery().

// Construct a keyboard.
const inlineKeyboard = new InlineKeyboard().text("click", "click-payload");

// Send a keyboard along with a message.
bot.command("start", async (ctx) => {
  await ctx.reply("Curious? Click me!", { reply_markup: inlineKeyboard });
});

// Wait for click events with specific callback data.
bot.callbackQuery("click-payload", async (ctx) => {
  await ctx.answerCallbackQuery({
    text: "You were curious, indeed!",
  });
});

Answering All Callback Queries

bot.callbackQuery() is useful to listen for click events of specific buttons. You can use bot.on('callback_query:data') to listen for events of any button.

bot.callbackQuery("click-payload" /* , ... */);

bot.on("callback_query:data", async (ctx) => {
  console.log("Unknown button event with payload", ctx.callbackQuery.data);
  await ctx.answerCallbackQuery(); // remove loading animation
});

It makes sense to define bot.on('callback_query:data') at last to always answer all other callback queries that your previous listeners did not handle. Otherwise, some clients may display a loading animation for up to a minute when a user presses a button that your bot does not want to react to.

Custom Keyboards

First things first: custom keyboards are sometimes just called keyboards, sometimes they’re called reply keyboards, and even Telegram’s own documentation is not consistent in this respect. As a simple rule of thumb, when it isn’t absolutely obvious from the context and not called inline keyboard, it probably is a custom keyboard. This refers to a way to replace the system keyboard by a set of buttons that you can define.

Revisit the custom keyboard section in the Introduction for Developersopen in new window written by the Telegram team.

grammY has a simple and intuitive way to build up the custom keyboards that your bot can use to replace the system keyboard. It provides a class called Keyboard for this.

Once a user clicks a text button, your bot will receive the sent text as a plain text message. Remember that you can listen for text message via bot.on('message:text') or bot.hears().

Building a Custom Keyboard

Here are three examples how to build a custom keyboard with text buttons.

You can also request the phone number with requestContact, the location with requestLocation, and a poll with requestPoll.

Example 1

Three buttons in one column can be built like this:

Code
const keyboard = new Keyboard()
  .text("Yes, they certainly are").row()
  .text("I'm not quite sure").row()
  .text("No. 😈");
Result

Example 1

Example 2

A calculator pad can be built like this:

Code
const keyboard = new Keyboard()
  .text("7").text("8").text("9").text("*").row()
  .text("4").text("5").text("6").text("/").row()
  .text("1").text("2").text("3").text("-").row()
  .text("0").text(".").text("=").text("+");
Result

Example 2

Example 3

Four buttons in a grid can be built like this:

Code
const keyboard = new Keyboard()
  .text("A").text("B").row()
  .text("C").text("D");
Result

Example 3

Sending a Custom Keyboard

You can send a custom keyboard directly along a message, no matter whether you use bot.api.sendMessage, ctx.api.sendMessage, or ctx.reply:

// Send keyboard with message.
await ctx.reply(text, {
  reply_markup: keyboard,
});

Naturally, all other methods that send messages other than text messages support the same options, as specified by the Telegram Bot API Referenceopen in new window.

If you want to specify further options with your message, you may need to create your own reply_markup object. In that case, you have to use keyboard.build() when passing it to your object.

Resize Custom Keyboard

You can specify the resize_keyboard option if you want the custom keyboard to be resized according to the buttons it contains. This will effectively make the keyboard smaller. (Usually, the keyboard will always have the size of the app’s standard keyboard.)

await ctx.reply(text, {
  reply_markup: {
    resize_keyboard: true,
    keyboard: keyboard.build(),
  },
});

One-Time Custom Keyboards

You can specify the one_time_keyboard option if you want the custom keyboard to be hidden immediately after the first button was pressed.

await ctx.reply(text, {
  reply_markup: {
    one_time_keyboard: true,
    keyboard: keyboard.build(),
  },
});

Input Field Placeholder

You can specify the input_field_placehoder option if you want a placeholder to be shown in the input field as long as the custom keyboard is visible.

const keyboard = new Keyboard().text("LEFT").text("RIGHT");

await ctx.reply(text, {
  reply_markup: {
    input_field_placehoder: "Send LEFT or RIGHT",
    keyboard: keyboard.build(),
  },
});

Selectively Send Custom Keyboards

You can specify the selective option if you want to show the custom keyboard only to those users that are @-mentioned in the text of the message object, and to the sender of the original message in case your message is a reply.

await ctx.reply(text, {
  reply_to_message_id: ctx.msg.message_id,
  reply_markup: {
    selective: true,
    keyboard: keyboard.build(),
  },
});

Responding to Clicks

As mentioned earlier, all that custom keyboards do is sending regular text messages. Your bot cannot differentiate between ordinary text messages, and text messages that were sent by clicking a button.

Moreover, buttons will always send exactly the message that’s written on them. Telegram does not allow you to create buttons that display one text, but send another. If you need to do this, you should use an inline keyboard instead.

In order to handle the click of a specific button, you can use bot.hears with the same text as you put on the button. If you want to handle all button clicks at once, you use bot.on('message:text') and inspect ctx.msg.text to figure out which button was clicked, or if an ordinary text message was sent.

Removing a Custom Keyboard

Unless you specify one_time_keyboard as described above, the custom keyboard will remain open for the user (but the user can minimize it).

You can only remove a custom keyboard when you send a new message in the chat, just like you can only specify a new keyboard by sending a message. Pass { remove_keyboard: true } as reply_markup like so:

await ctx.reply(text, {
  reply_markup: { remove_keyboard: true },
});

Next to remove_keyboard, you can again set selective: true in order to remove the custom keyboard for selected users only. This works analogously to selectively sending a custom keyboard.

Plugin Summary

This plugin is built-in into the core of grammY. You don’t need to install anything to use it. Simply import everything from grammY itself.

Also, both the documentation and the API reference of this plugin are unified with the core package.