Skip to content

Keyboards and Callbacks

maule edited this page Aug 16, 2026 · 1 revision

Keyboards and Callbacks

Learn how to create interactive keyboards and handle button clicks.

Inline Keyboards

Inline keyboards are buttons that appear directly in the message.

Creating Inline Keyboards

$keyboard = $bot->buildKeyboardOfInline([
    "Button 1" => "callback_data_1",
    "Button 2" => "callback_data_2",
    "Button 3" => "callback_data_3",
]);

$bot->sendMessage(
    chatId: $chatId,
    text: "Choose an option:",
    keyboard: $keyboard
);

Handling Button Clicks

// If user clicks "Button 1"
$bot->simpleCallbackResponse("callback_data_1", "You clicked Button 1!");

// With more options
$bot->simpleCallbackResponse("callback_data_1", [
    "text" => "Button 1 was clicked!",
    "photo" => "result.png",
]);

Link Keyboards

Buttons that link to URLs.

$links = $bot->buildKeyboardOfLinks([
    "Visit GitHub" => "https://github.com",
    "Telegram" => "https://telegram.org",
    "Documentation" => "https://example.com/docs",
]);

$bot->sendMessage(
    chatId: $chatId,
    text: "Useful links:",
    keyboard: $links
);

Merging Keyboards

Combine multiple keyboards.

Two Keyboards

$buttons = $bot->buildKeyboardOfInline([
    "Option A" => "opt_a",
    "Option B" => "opt_b",
]);

$links = $bot->buildKeyboardOfLinks([
    "Learn More" => "https://example.com",
]);

$merged = $bot->mergeKeyboards($buttons, $links);

$bot->sendMessage($chatId, "Choose:", keyboard: $merged);

Multiple Keyboards

$kb1 = $bot->buildKeyboardOfInline(["Start" => "start"]);
$kb2 = $bot->buildKeyboardOfInline(["Help" => "help"]);
$kb3 = $bot->buildKeyboardOfLinks(["Docs" => "https://docs.example.com"]);

$full = $bot->mergeMultipleKeyboards([$kb1, $kb2, $kb3]);

$bot->sendMessage($chatId, "Menu:", keyboard: $full);

Multi-Button Menus

Create structured menus.

Two Columns

$keyboard = [
    'inline_keyboard' => [
        [
            ['text' => 'Button 1', 'callback_data' => 'btn1'],
            ['text' => 'Button 2', 'callback_data' => 'btn2'],
        ],
        [
            ['text' => 'Button 3', 'callback_data' => 'btn3'],
            ['text' => 'Button 4', 'callback_data' => 'btn4'],
        ],
    ],
];

$bot->sendMessage($chatId, "Menu:", keyboard: $keyboard);

Navigation Menu

$mainMenu = $bot->buildKeyboardOfInline([
    "Start" => "start",
    "Help" => "help",
    "Settings" => "settings",
]);

$backButton = $bot->buildKeyboardOfInline([
    "← Back" => "back",
]);

$settingsMenu = $bot->buildKeyboardOfInline([
    "Language" => "lang",
    "Theme" => "theme",
]);

$settingsMenuWithBack = $bot->mergeKeyboards($settingsMenu, $backButton);

// Show main menu
$bot->commandSimple("/start", [
    "text" => "Main Menu",
    "keyboard" => $mainMenu,
]);

// Show settings menu
$bot->simpleCallbackResponse("settings", [
    "text" => "Settings",
    "keyboard" => $settingsMenuWithBack,
]);

// Back to main
$bot->simpleCallbackResponse("back", [
    "text" => "Main Menu",
    "keyboard" => $mainMenu,
]);

Editing Messages

Replace message content when button is clicked.

Edit Text

$keyboard = $bot->buildKeyboardOfInline([
    "Click me" => "click",
]);

$bot->commandSimple("/start", [
    "text" => "Original text",
    "keyboard" => $keyboard,
]);

// When button is clicked, edit the message
$bot->simpleCallbackResponse("click", [
    "text" => "This message was edited!",
], edit: true);

Edit Photo

$bot->commandSimple("/start", [
    "text" => "Photo gallery",
    "photo" => "photo1.jpg",
    "keyboard" => $bot->buildKeyboardOfInline([
        "Next →" => "next_photo",
    ]),
]);

$bot->simpleCallbackResponse("next_photo", [
    "photo" => "photo2.jpg",
    "text" => "Photo 2",
], edit: true);

Sending New Messages

Instead of editing, send a new message.

$bot->simpleCallbackResponse("button", [
    "text" => "New message sent!",
], edit: false); // Create new message

Use edit: false when:

  • You want to keep the original message
  • Creating a sequence of messages
  • Showing results/progress

Use edit: true (default) when:

  • You want to replace the menu
  • Saving space in chat
  • Showing loading states

Callback Data Best Practices

Simple Data

$keyboard = $bot->buildKeyboardOfInline([
    "Yes" => "yes",
    "No" => "no",
]);

JSON Data (if needed)

$data = json_encode(['action' => 'delete', 'id' => 123]);
$keyboard = [
    'inline_keyboard' => [[
        ['text' => 'Delete', 'callback_data' => $data],
    ]],
];

Advanced Examples

Confirmation Dialog

$bot->commandSimple("/delete", [
    "text" => "Are you sure?",
    "keyboard" => $bot->buildKeyboardOfInline([
        "Yes, delete" => "confirm_delete",
        "Cancel" => "cancel",
    ]),
]);

$bot->simpleCallbackResponse("confirm_delete", [
    "text" => "Item deleted!",
]);

$bot->simpleCallbackResponse("cancel", [
    "text" => "Cancelled",
]);

Numbered Menu

$items = [
    "Item 1" => "item_1",
    "Item 2" => "item_2",
    "Item 3" => "item_3",
];

$menu = $bot->buildKeyboardOfInline($items);

$bot->commandSimple("/list", [
    "text" => "Choose an item:",
    "keyboard" => $menu,
]);

foreach ($items as $name => $callback) {
    $bot->simpleCallbackResponse($callback, "You selected: $name");
}

Pagination

class BotPages {
    private $currentPage = 1;
    
    public function showPage($bot, $chatId, $pageNum) {
        $items = ["Page 1 content", "Page 2 content", "Page 3 content"];
        $text = $items[$pageNum - 1] ?? "Not found";
        
        $keyboard = [
            'inline_keyboard' => [[
                $pageNum > 1 ? ['text' => '← Prev', 'callback_data' => "page_" . ($pageNum - 1)] : null,
                ['text' => "Page $pageNum", 'callback_data' => "page_info"],
                $pageNum < 3 ? ['text' => 'Next →', 'callback_data' => "page_" . ($pageNum + 1)] : null,
            ]],
        ];
        
        $keyboard['inline_keyboard'][0] = array_filter($keyboard['inline_keyboard'][0]);
        
        $bot->sendMessage($chatId, $text, keyboard: $keyboard);
    }
}

Tips & Tricks

  1. Keep callback data short - Limited to 64 bytes
  2. Use descriptive names - Makes debugging easier
  3. Clear feedback - Always respond to callbacks
  4. Handle errors - Check if button still exists
  5. Rate limiting - Prevent spam clicks with timestamps

See also: API Reference, Examples

Clone this wiki locally