MainButton и BackButton: нативные элементы Telegram

Подключишь нативную MainButton внизу экрана и BackButton в шапке Mini App.

Нативные кнопки Telegram

MainButton — кнопка в нижней панели клиента Telegram (над системной областью). BackButton — кнопка «назад» в шапке WebView. Обе рисует Telegram, не твой DOM: они выглядят нативно и не перекрываются твоим CSS случайно.

С Bot API 7.10+ у нижней панели также есть Secondary Button; в большинстве туториалов достаточно MainButton + BackButton.


MainButton для подтверждения, BackButton для навигации

typescript
// Telegram WebApp API (MainButton / BackButton), script: telegram-web-app.js

type WebApp = {
  MainButton: {
    text: string;
    isVisible: boolean;
    isActive: boolean;
    setText: (text: string) => WebApp["MainButton"];
    show: () => WebApp["MainButton"];
    hide: () => WebApp["MainButton"];
    enable: () => WebApp["MainButton"];
    disable: () => WebApp["MainButton"];
    showProgress: (leaveActive?: boolean) => WebApp["MainButton"];
    hideProgress: () => WebApp["MainButton"];
    onClick: (cb: () => void) => WebApp["MainButton"];
    offClick: (cb: () => void) => WebApp["MainButton"];
    setParams: (params: {
      text?: string;
      color?: string;
      text_color?: string;
      is_active?: boolean;
      is_visible?: boolean;
    }) => WebApp["MainButton"];
  };
  BackButton: {
    isVisible: boolean;
    show: () => WebApp["BackButton"];
    hide: () => WebApp["BackButton"];
    onClick: (cb: () => void) => WebApp["BackButton"];
    offClick: (cb: () => void) => WebApp["BackButton"];
  };
  ready: () => void;
  HapticFeedback: {
    impactOccurred: (style: "light" | "medium" | "heavy" | "rigid" | "soft") => void;
  };
};

const tg = window.Telegram.WebApp as unknown as WebApp;
tg.ready();

function goToScreen(screen: "list" | "details") {
  if (screen === "details") {
    tg.BackButton.show();
    tg.MainButton.setParams({
      text: "Подтвердить",
      is_visible: true,
      is_active: true,
    });
  } else {
    tg.BackButton.hide();
    tg.MainButton.hide();
  }
}

const onMain = () => {
  tg.MainButton.showProgress();
  // отправка формы / вызов API
  void submitOrder()
    .then(() => {
      tg.HapticFeedback.impactOccurred("medium");
      goToScreen("list");
    })
    .finally(() => {
      tg.MainButton.hideProgress();
    });
};

const onBack = () => {
  goToScreen("list");
};

tg.MainButton.onClick(onMain);
tg.BackButton.onClick(onBack);

goToScreen("details");

async function submitOrder(): Promise<void> {
  await fetch("/api/order", { method: "POST" });
}

Как это работает

  • MainButton.setParams / setText + show — текст и видимость нижней кнопки.
  • onClick — обработчик нажатия (альтернатива: WebApp.onEvent('mainButtonClicked', ...)).
  • showProgress / hideProgress — спиннер на кнопке на время async-операции.
  • BackButton.show() — появляется стрелка назад в хедере Telegram; по умолчанию скрыта.
  • Свой HTML-footer дублировать не нужно — нативная кнопка уже занимает нижнюю зону.

Частые ошибки

Рисуешь свою sticky-кнопку поверх MainButton → двойной UI и конфликты с safe-area. Для главного CTA используй MainButton.

Не снимаешь onClick при размонтировании (SPA) → утечки обработчиков. В React — offClick в cleanup useEffect.

Ждёшь BackButton на корневом экране → она для «внутренних» экранов. На корне обычно hide().


Что дальше

Материалы gramdocs.tech носят образовательный характер и не являются финансовой, юридической или инвестиционной рекомендацией. Работа с блокчейном TON и токеном Gram связана с рисками потери средств. Правовая информация