~/wiki / telegram-boty / navigatsiya-telegram-bot-knopki-menyu

Как сделать навигацию в Telegram-боте: inline-кнопки, reply-меню, команды и Mini App — полное руководство

Основной чат

Чат для вайбкодеров: новости, гайды, поиск исполнителей, маркетплейс и разбор реальных кейсов.

$ cd раздел/ $ join vibe dev
Как сделать навигацию в Telegram-боте: inline-кнопки, reply-меню, команды и Mini App — полное руководство - обложка

Навигация в Telegram-боте — это набор интерфейсных элементов, с помощью которых пользователь перемещается между разделами бота, запускает нужные функции и получает ответы без необходимости что-то печатать вручную.

Хорошая навигация в боте работает по одному принципу: пользователь всегда видит, что можно сделать дальше, и добирается до нужного действия не более чем за два-три нажатия.

В Telegram есть несколько видов навигационных элементов:

Элемент Где отображается Главная особенность
Inline-кнопки Под конкретным сообщением Привязаны к сообщению, меняются динамически
Reply-клавиатура Над полем ввода Постоянно видны, заменяют клавиатуру
Команды Меню «/» у поля ввода Работают всегда, из любого состояния
Кнопка Menu Слева от поля ввода Открывает список команд или Mini App
Mini App Полноэкранный веб-интерфейс Полноценный сайт внутри Telegram

Inline-кнопки: самый гибкий вид навигации

Что это и когда использовать

Inline-кнопки прикрепляются к конкретному сообщению или медиафайлу и остаются рядом с ним. Это самый популярный вид навигации в современных ботах — они не перекрывают экран, появляются там где нужны, и могут меняться в ответ на действия пользователя.

Используйте inline-кнопки когда:

  • нужно предложить варианты ответа в конкретный момент диалога
  • требуется пагинация (список товаров, статей, результатов)
  • нужны кнопки «Подтвердить / Отменить» для опасных действий
  • хотите обновлять содержимое сообщения без отправки нового

Inline-кнопки на Python (aiogram 3)

python
from aiogram import Bot, Dispatcher, F
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
from aiogram.filters import CommandStart

bot = Bot(token="YOUR_BOT_TOKEN")
dp = Dispatcher()

# Главное меню
def main_menu() -> InlineKeyboardMarkup:
    return InlineKeyboardMarkup(inline_keyboard=[
        [
            InlineKeyboardButton(text="📦 Каталог", callback_data="catalog"),
            InlineKeyboardButton(text="🛒 Корзина", callback_data="cart"),
        ],
        [
            InlineKeyboardButton(text="📞 Поддержка", callback_data="support"),
            InlineKeyboardButton(text="⚙️ Настройки", callback_data="settings"),
        ],
    ])

@dp.message(CommandStart())
async def start(message):
    await message.answer(
        "Привет! Выберите раздел:",
        reply_markup=main_menu()
    )

# Обработка нажатий
@dp.callback_query(F.data == "catalog")
async def show_catalog(callback: CallbackQuery):
    # Обновляем сообщение вместо отправки нового
    await callback.message.edit_text(
        "📦 Каталог товаров:",
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="← Назад", callback_data="back_main")],
        ])
    )
    await callback.answer()  # убираем "часики" на кнопке

@dp.callback_query(F.data == "back_main")
async def back_to_main(callback: CallbackQuery):
    await callback.message.edit_text(
        "Главное меню:",
        reply_markup=main_menu()
    )
    await callback.answer()

Inline-кнопки на Node.js (grammY)

javascript
const { Bot, InlineKeyboard } = require('grammy');
const bot = new Bot('YOUR_BOT_TOKEN');

// Функция создания главного меню
function mainMenu() {
    return new InlineKeyboard()
        .text('📦 Каталог', 'catalog').text('🛒 Корзина', 'cart').row()
        .text('📞 Поддержка', 'support').text('⚙️ Настройки', 'settings');
}

bot.command('start', async (ctx) => {
    await ctx.reply('Привет! Выберите раздел:', {
        reply_markup: mainMenu(),
    });
});

bot.callbackQuery('catalog', async (ctx) => {
    await ctx.editMessageText('📦 Каталог товаров:', {
        reply_markup: new InlineKeyboard().text('← Назад', 'back_main'),
    });
    await ctx.answerCallbackQuery();
});

bot.callbackQuery('back_main', async (ctx) => {
    await ctx.editMessageText('Главное меню:', {
        reply_markup: mainMenu(),
    });
    await ctx.answerCallbackQuery();
});

Кнопка-ссылка в inline-меню

Inline-кнопка может открывать не просто действие, а внешнюю ссылку или сайт:

python
InlineKeyboardButton(
    text="🌐 Открыть сайт",
    url="https://your-site.ru"
)

Reply-клавиатура: постоянное меню под полем ввода

Что это и когда использовать

Reply-кнопки выглядят как шаблонные заранее заготовленные ответы и закрепляются вместо основной клавиатуры на экране устройства. Обычно используются в чат-ботах в качестве основного меню.

Они всегда остаются под полем ввода, хорошо видны и занимают часть экрана, но у них есть существенный минус: они могут легко потеряться и скрыться. Пользователи иногда случайно убирают клавиатуру свайпом и не понимают куда она делась.

Используйте reply-клавиатуру когда:

  • бот простой с фиксированным набором действий
  • аудитория не технически подкованная, нужны постоянно видимые кнопки
  • навигация не меняется в зависимости от контекста
python
from aiogram.types import ReplyKeyboardMarkup, KeyboardButton, ReplyKeyboardRemove

def main_reply_keyboard() -> ReplyKeyboardMarkup:
    return ReplyKeyboardMarkup(
        keyboard=[
            [KeyboardButton(text="📦 Каталог"), KeyboardButton(text="🛒 Корзина")],
            [KeyboardButton(text="📞 Поддержка"), KeyboardButton(text="ℹ️ О нас")],
        ],
        resize_keyboard=True,   # компактный размер
        one_time_keyboard=False # не скрывать после нажатия
    )

@dp.message(CommandStart())
async def start(message):
    await message.answer(
        "Выберите раздел:",
        reply_markup=main_reply_keyboard()
    )

# Обработка нажатия по тексту кнопки
@dp.message(F.text == "📦 Каталог")
async def catalog(message):
    await message.answer("Открываю каталог...")

# Убрать клавиатуру когда не нужна
@dp.message(F.text == "Скрыть меню")
async def hide_keyboard(message):
    await message.answer(
        "Клавиатура скрыта",
        reply_markup=ReplyKeyboardRemove()
    )

Команды: навигация через /start, /help, /menu

Что это и зачем

Чтобы воспользоваться меню, нужно нажать на иконку слева от поля ввода сообщения или написать слеш — бот подскажет доступные команды. У таких команд высший приоритет: они будут выполнены, даже если пользователь находился в другой цепочке.

Команды — это страховочная сеть навигации. Даже если пользователь запутался в кнопках или потерял меню, /start всегда вернёт его в начало.

Настройка команд через BotFather

plaintext
/setcommands
→ выбрать бота
→ ввести список:

start - Начало работы
menu - Главное меню
catalog - Каталог товаров
cart - Моя корзина
help - Помощь
settings - Настройки

После этого пользователь видит подсказки команд при вводе /.

Обработка команд в коде

python
from aiogram.filters import Command

@dp.message(Command("menu"))
async def show_menu(message):
    await message.answer("Главное меню:", reply_markup=main_menu())

@dp.message(Command("help"))
async def help_command(message):
    await message.answer(
        "Доступные команды:\n"
        "/start — начало работы\n"
        "/menu — главное меню\n"
        "/catalog — каталог\n"
        "/cart — корзина\n"
        "/help — эта справка"
    )

Кнопка Menu: фиксированный вход в бота

Кнопка Menu — это кнопка слева от поля ввода сообщения (иконка сетки или стрелка). Она открывает список команд или Mini App. Настраивается через BotFather командой /setmenubutton.

Два режима:

  • Список команд — открывает то же меню команд со слешем
  • Mini App — открывает веб-приложение прямо из кнопки Menu
python
# Программная установка кнопки Menu через Bot API
from aiogram.types import MenuButtonWebApp, WebAppInfo

await bot.set_chat_menu_button(
    menu_button=MenuButtonWebApp(
        text="Открыть приложение",
        web_app=WebAppInfo(url="https://your-site.ru/app")
    )
)

Mini App: полноценный интерфейс внутри Telegram

Mini App — это веб-приложение (HTML/CSS/JS), которое открывается внутри Telegram как полноэкранный интерфейс. Подходит когда логика слишком сложна для кнопок: каталог с фильтрами, форма заказа, личный кабинет.

html
<!-- Минимальная структура Mini App -->
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://telegram.org/js/telegram-web-app.js"></script>
</head>
<body>
    <script>
        const tg = window.Telegram.WebApp;
        tg.ready(); // инициализация

        // Цвет интерфейса из темы Telegram
        document.body.style.background = tg.themeParams.bg_color;

        // Главная кнопка внизу экрана
        tg.MainButton.setText('Оформить заказ');
        tg.MainButton.show();
        tg.MainButton.onClick(() => {
            // Отправляем данные в бота
            tg.sendData(JSON.stringify({ action: 'order', items: [] }));
        });
    </script>
</body>
</html>

Как выстроить правильную структуру навигации

Хорошая навигация в Telegram-боте строится по трём правилам.

Правило глубины. Не больше трёх уровней вложенности: главное меню → раздел → действие. Если глубже — пользователь теряется и не возвращается.

Правило кнопки «Назад». На каждом уровне глубже первого должна быть кнопка возврата. Пользователь не должен тыкать /start чтобы выбраться из подраздела.

Правило /start. Команда /start всегда возвращает в самое начало. Это абсолютный сброс, который должен работать из любого состояния.

python
# Универсальная структура с кнопкой Назад
def catalog_menu() -> InlineKeyboardMarkup:
    return InlineKeyboardMarkup(inline_keyboard=[
        [InlineKeyboardButton(text="👕 Одежда",     callback_data="cat_clothes")],
        [InlineKeyboardButton(text="👟 Обувь",      callback_data="cat_shoes")],
        [InlineKeyboardButton(text="🎒 Аксессуары", callback_data="cat_accessories")],
        [InlineKeyboardButton(text="← Главное меню", callback_data="back_main")],
    ])

Сравнение: что выбрать под задачу

Задача Рекомендуемый тип
Простой бот с 3–5 разделами Reply-клавиатура
Многоуровневая навигация Inline-кнопки
Выбор из списка / пагинация Inline-кнопки
Подтверждение действия Inline-кнопки
Кнопки с ссылками на сайт Inline-кнопки с URL
Быстрый доступ из любого места Команды через BotFather
Сложный интерфейс с формами Mini App
Корзина и оплата Mini App

Частые ошибки в навигации Telegram-ботов

Слишком много кнопок в ряду. Telegram отображает inline-кнопки адаптивно, но больше 3 кнопок в одной строке — это некрасиво на мобильном. Оптимально 1–2 кнопки в строке.

Нет кнопки «Назад». Пользователь зашёл в подраздел и не знает как вернуться. Потери конверсии гарантированы.

Reply-клавиатура скрывается случайно. Добавьте команду /menu которая всегда показывает клавиатуру заново. Пользователи, случайно скрывшие её, найдут способ вернуть.

Callback_data не уникальны. Если двум кнопкам присвоить одинаковый callback_data — они будут делать одно и то же. Используйте паттерн action:id: cat:12, page:3.

python
# Хорошо: уникальные идентификаторы с контекстом
InlineKeyboardButton(text="Следующая →", callback_data="page:2")
InlineKeyboardButton(text="Товар 15",    callback_data="item:15")

# Плохо: одинаковые для разных действий
InlineKeyboardButton(text="Следующая →", callback_data="next")
InlineKeyboardButton(text="Назад",       callback_data="back")
# если используете несколько уровней — 'back' сработает непредсказуемо

Отсутствует answerCallbackQuery. Если не вызвать callback.answer() после обработки — у пользователя на кнопке будет крутиться «часики» несколько секунд. Всегда отвечайте на callback, даже пустым ответом.


Чеклист навигации в Telegram-боте

plaintext
Структура:
☐ Не более 3 уровней вложенности
☐ На каждом уровне (кроме первого) есть кнопка «Назад»
☐ /start всегда возвращает в главное меню
☐ /help показывает список доступных команд

Inline-кнопки:
☐ Не более 2–3 кнопок в одной строке
☐ callback_data уникальны и имеют паттерн action:id
☐ Каждый обработчик вызывает callback.answer()
☐ edit_message используется вместо отправки нового сообщения где возможно

Команды:
☐ Список команд задан через BotFather (/setcommands)
☐ Команды задокументированы кратко и понятно

Reply-клавиатура:
☐ resize_keyboard=True для компактного отображения
☐ Есть способ восстановить клавиатуру если пользователь её скрыл

Итог

Навигация в Telegram-боте строится из четырёх инструментов: inline-кнопки для динамических сценариев, reply-клавиатура для постоянного меню, команды как аварийный выход из любого состояния, Mini App для сложных интерфейсов.

Для большинства проектов достаточно inline-кнопок плюс три-четыре команды через BotFather. Reply-клавиатура подходит для простых ботов с постоянной структурой. Mini App — когда логика переросла возможности кнопочного интерфейса.

Главный принцип — пользователь никогда не должен «застрять»: всегда виден следующий шаг и всегда есть путь назад.


FAQ

Можно ли использовать inline-кнопки и reply-клавиатуру одновременно? Да. При использовании inline-кнопок пользователь видит не только их, но и основную клавиатуру. Это нормальная практика: reply-клавиатура для основного меню, inline-кнопки для контекстных действий внутри диалога.

Сколько кнопок максимум можно поставить? Технически — до 100 inline-кнопок на сообщение. Практически — не более 8–10: больше не помещается на экране и запутывает пользователя.

Как сделать кнопку которая открывает сайт? Inline-кнопка с параметром url вместо callback_data. Нажатие открывает внешнюю ссылку в браузере или Mini App если используется web_app.

Можно ли менять кнопки после отправки? Да — через edit_message_reply_markup (только клавиатура) или edit_message_text (текст и клавиатура). Это ключевое преимущество inline-кнопок перед reply-клавиатурой.

Что делать если пользователь потерял reply-клавиатуру? Добавить команду /menu которая отправляет новое сообщение с reply_markup=main_reply_keyboard(). Также можно добавить inline-кнопку «Показать меню» в приветственном сообщении.


Актуально для aiogram 3.x, grammY 1.x, Bot API 9.x. Июнь 2026.

$ cd ../ ← назад к Telegram-боты