БлогУслугиКарьера
Обсудить проект
БлогУслугиКарьераОбсудить проект
Mini Apps

Вы пишете Telegram Mini App неправильно. Вот доказательство

Большинство туториалов по @telegram-apps/sdk-react учат неправильно. Разбираем initDataUnsafe, MainButton, BackButton — с кодом и объяснением, что на самом деле происходит.

Редакция Feature
Редакция Feature·
3 апр
·
12 мин
·
Вы пишете Telegram Mini App неправильно. Вот доказательство

Если вы когда-нибудь пытались разобраться с @telegram-apps/sdk-react по официальной документации — вы знаете, как это бывает. Половина примеров устарела, TypeScript ругается на типы, а в чате поддержки советуют «почитать исходники».

Эта статья — то, что хотелось бы иметь при первом знакомстве с SDK. Разбираем всё по порядку: установка, инициализация, работа с UI-компонентами Telegram, получение данных пользователя и типичные ошибки, которые встречаются почти в каждом проекте.

Что такое @telegram-apps/sdk-react и зачем он нужен

Telegram Mini Apps работают через WebApp API — набор методов, которые Telegram встраивает в JavaScript-окружение браузера. Напрямую работать с window.Telegram.WebApp можно, но неудобно: нет типов, нет реактивности, код быстро превращается в кашу.

@telegram-apps/sdk-react — это обёртка над WebApp API с React-хуками, TypeScript-типами и удобными компонентами. Он берёт на себя инициализацию и даёт вам чистый декларативный интерфейс.

Основные возможности:

  • Хуки для работы с MainButton, BackButton, SettingsButton
  • Реактивный доступ к данным пользователя (initDataUnsafe)
  • Управление Viewport и цветовой схемой
  • Работа с CloudStorage
  • Поддержка платежей через Invoice

Установка и настройка проекта

Создаём проект. Рекомендуется Vite + React + TypeScript:

npm create vite@latest my-mini-app -- --template react-ts
cd my-mini-app
npm install

Устанавливаем SDK:

npm install @telegram-apps/sdk-react @telegram-apps/sdk

Для разработки без Telegram-клиента понадобится эмулятор:

npm install -D @telegram-apps/telegram-mock

Структура проекта после установки:

src/
├── components/
├── hooks/
├── App.tsx
├── main.tsx
└── mockEnv.ts   # для разработки

Инициализация SDK

Это первое, что нужно сделать до любого использования хуков. Инициализация должна произойти до монтирования React-дерева.

// src/main.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import { init, isTMA } from "@telegram-apps/sdk-react";
import App from "./App";

async function bootstrap() {
  // В dev-режиме эмулируем окружение Telegram
  if (import.meta.env.DEV) {
    const { mockTelegramEnv, parseInitData } =
      await import("@telegram-apps/telegram-mock");

    mockTelegramEnv({
      themeParams: {
        accentTextColor: "#6ab2f2",
        bgColor: "#17212b",
        buttonColor: "#5288c1",
        buttonTextColor: "#ffffff",
        headerBgColor: "#17212b",
        hintColor: "#708499",
        linkColor: "#6ab3f3",
        secondaryBgColor: "#232e3c",
        sectionBgColor: "#17212b",
        sectionHeaderTextColor: "#6ab3f3",
        subtitleTextColor: "#708499",
        textColor: "#f5f5f5",
      },
      initDataRaw: new URLSearchParams([
        [
          "user",
          JSON.stringify({
            id: 123456789,
            first_name: "Иван",
            last_name: "Петров",
            username: "ivan_petrov",
            language_code: "ru",
          }),
        ],
        ["hash", "test_hash"],
      ]).toString(),
    });
  }

  // Проверяем, что мы внутри Telegram
  if (isTMA("simple")) {
    await init();
  }

  ReactDOM.createRoot(document.getElementById("root")!).render(
    <React.StrictMode>
      <App />
    </React.StrictMode>
  );
}

bootstrap();

Важный момент: init() — асинхронная функция. Не вызывайте хуки SDK до её завершения, иначе получите ошибку SDK is not initialized.

SDKProvider — оборачиваем приложение

// src/App.tsx
import { SDKProvider } from "@telegram-apps/sdk-react";
import { MainScreen } from "./components/MainScreen";

export default function App() {
  return (
    <SDKProvider acceptCustomStyles debug={import.meta.env.DEV}>
      <MainScreen />
    </SDKProvider>
  );
}

acceptCustomStyles — позволяет Telegram применять свои CSS-переменные для темизации. Рекомендуется включать всегда.

Получение данных пользователя: initDataUnsafe

Это самый частый вопрос в поиске — initDataUnsafe @telegram-apps/sdk. Разбираем подробно.

import { useInitData } from "@telegram-apps/sdk-react";

function UserProfile() {
  const initData = useInitData();

  if (!initData) {
    return <div>Загрузка...</div>;
  }

  const { user } = initData;

  if (!user) {
    return <div>Пользователь не определён</div>;
  }

  return (
    <div>
      <p>
        Имя: {user.firstName} {user.lastName}
      </p>
      <p>Username: @{user.username}</p>
      <p>ID: {user.id}</p>
      <p>Язык: {user.languageCode}</p>
    </div>
  );
}

Почему "Unsafe"? Потому что данные из initDataUnsafe не верифицированы на клиенте. Для продакшена обязательно проверяйте initData.hash на бэкенде:

# Пример верификации на Python (бэкенд)
import hashlib
import hmac

def verify_init_data(init_data: str, bot_token: str) -> bool:
    parsed = dict(item.split('=', 1) for item in init_data.split('&'))
    received_hash = parsed.pop('hash', '')

    data_check_string = '\n'.join(
        f'{k}={v}' for k, v in sorted(parsed.items())
    )

    secret_key = hmac.new(
        b'WebAppData',
        bot_token.encode(),
        hashlib.sha256
    ).digest()

    expected_hash = hmac.new(
        secret_key,
        data_check_string.encode(),
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(received_hash, expected_hash)

Нужна Telegram Mini App?

Разработаем Mini App под ваши задачи — от простого каталога до полноценного магазина

Заказать Mini App

MainButton — кнопка в нижней панели

MainButton — одна из самых используемых фич. Это кнопка, которая появляется в нижней части экрана Telegram.

import { useMainButton } from "@telegram-apps/sdk-react";
import { useEffect } from "react";

function OrderForm() {
  const mainButton = useMainButton();

  useEffect(() => {
    mainButton.setParams({
      text: "Оформить заказ",
      isVisible: true,
      isEnabled: true,
      backgroundColor: "#bdfe52",
      textColor: "#000000",
    });

    const handleClick = () => {
      mainButton.showProgress(); // Показываем загрузку
      submitOrder().then(() => {
        mainButton.hideProgress();
        mainButton.setParams({ text: "✓ Заказ оформлен", isEnabled: false });
      });
    };

    mainButton.on("click", handleClick);

    return () => {
      mainButton.off("click", handleClick);
      mainButton.hide(); // Скрываем при размонтировании
    };
  }, [mainButton]);

  return <form>{/* Поля формы */}</form>;
}

Типичная ошибка: не скрывать MainButton при размонтировании компонента. Кнопка останется видимой на других экранах.

BackButton — навигация назад

import { useBackButton } from "@telegram-apps/sdk-react";
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";

function ProductPage() {
  const backButton = useBackButton();
  const navigate = useNavigate();

  useEffect(() => {
    backButton.show();

    const handleBack = () => navigate(-1);
    backButton.on("click", handleBack);

    return () => {
      backButton.off("click", handleBack);
      backButton.hide();
    };
  }, [backButton, navigate]);

  return <div>{/* Содержимое страницы */}</div>;
}

Совет: выделите логику BackButton в кастомный хук, если используете роутинг:

// hooks/useTelegramBackButton.ts
import { useBackButton } from "@telegram-apps/sdk-react";
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";

export function useTelegramBackButton() {
  const backButton = useBackButton();
  const navigate = useNavigate();

  useEffect(() => {
    backButton.show();
    const handleBack = () => navigate(-1);
    backButton.on("click", handleBack);

    return () => {
      backButton.off("click", handleBack);
      backButton.hide();
    };
  }, [backButton, navigate]);
}

Viewport и адаптация к размеру экрана

Mini App запускается во встроенном WebView, размер которого может меняться (например, при открытии клавиатуры). Используйте useViewport чтобы реагировать на изменения:

import { useViewport } from "@telegram-apps/sdk-react";

function ResponsiveLayout() {
  const viewport = useViewport();

  // Расширяем на весь экран при старте
  useEffect(() => {
    viewport?.expand();
  }, [viewport]);

  const height = viewport?.height ?? window.innerHeight;
  const isExpanded = viewport?.isExpanded ?? false;

  return (
    <div style={{ height: `${height}px`, overflow: "hidden" }}>
      {/* Контент */}
    </div>
  );
}

CloudStorage — хранение данных без бэкенда

CloudStorage позволяет хранить небольшие данные (до 1024 байт на ключ, до 1024 ключей) напрямую в Telegram. Удобно для настроек, корзины покупок или черновиков:

import { useCloudStorage } from "@telegram-apps/sdk-react";

function CartStorage() {
  const cloudStorage = useCloudStorage();

  const saveCart = async (items: CartItem[]) => {
    await cloudStorage.set("cart", JSON.stringify(items));
  };

  const loadCart = async (): Promise<CartItem[]> => {
    const data = await cloudStorage.get("cart");
    return data ? JSON.parse(data) : [];
  };

  const clearCart = async () => {
    await cloudStorage.delete("cart");
  };

  return { saveCart, loadCart, clearCart };
}

Ограничение: CloudStorage — не замена базе данных. Данные привязаны к конкретному пользователю и конкретному боту, доступны только при открытой Mini App.

Обсудим ваш проект?

Оставьте контакты — перезвоним и обсудим задачу

Типичные ошибки и как их избежать

За несколько лет разработки Mini Apps мы собрали список ошибок, которые встречаются почти в каждом проекте.

Ошибка 1: Вызов хуков до инициализации

// ❌ Неправильно
function App() {
  const initData = useInitData(); // SDK ещё не инициализирован!
  // ...
}

// ✅ Правильно — дождаться init() в main.tsx до рендера

Ошибка 2: Не проверять наличие данных

// ❌ Упадёт если user === undefined
const { user } = useInitData()!;
console.log(user.firstName);

// ✅
const initData = useInitData();
if (!initData?.user) return null;
console.log(initData.user.firstName);

Ошибка 3: Забыть про cleanup в useEffect

// ❌ Обработчик не снимается
useEffect(() => {
  mainButton.on("click", handleClick);
}, []);

// ✅
useEffect(() => {
  mainButton.on("click", handleClick);
  return () => mainButton.off("click", handleClick);
}, [mainButton]);

Ошибка 4: Тестировать только в браузере

Mini App в браузере и Mini App в Telegram — разные вещи. Обязательно тестируйте через ngrok или аналоги с реальным ботом. Как это настроить — в статье как создать Telegram Mini App.

Деплой и настройка бота

После разработки нужно:

  1. Собрать проект: npm run build
  2. Разместить dist/ на хостинге с HTTPS (например, Vercel, Netlify или собственный VDS)
  3. В BotFather: /newapp или /editapp → установить URL вашего деплоя
  4. Проверить через https://t.me/ваш_бот?startapp=

Telegram требует HTTPS — HTTP не работает.

Итог

@telegram-apps/sdk-react значительно упрощает разработку Mini Apps по сравнению с прямой работой через window.Telegram.WebApp. Основные выводы:

  • Инициализируйте SDK до монтирования React-дерева
  • Всегда верифицируйте initData на бэкенде — никогда не доверяйте клиентским данным
  • Следите за cleanup в useEffect для MainButton и BackButton
  • Используйте mockTelegramEnv для разработки без постоянного деплоя
  • Тестируйте финальную версию в реальном Telegram-клиенте

Если хотите сэкономить время и запустить готовое Mini App без погружения в технические детали — расскажите нам о своём проекте, мы возьмём разработку на себя.


Читайте также

  • Как создать Telegram Mini App: полная инструкция
  • VK Mini Apps или Telegram: где бизнес зарабатывает больше в 2026
  • Telegram Bot API 2026: новые возможности

Обсудим ваш проект?

Оставьте контакты — перезвоним и обсудим задачу

Содержание
  • Что такое @telegram-apps/sdk-react и зачем он нужен
  • Установка и настройка проекта
  • Инициализация SDK
  • SDKProvider — оборачиваем приложение
  • Получение данных пользователя: initDataUnsafe
  • MainButton — кнопка в нижней панели
  • BackButton — навигация назад
  • Viewport и адаптация к размеру экрана
  • CloudStorage — хранение данных без бэкенда
  • Типичные ошибки и как их избежать
  • Деплой и настройка бота
  • Итог
  • Читайте также
Поделиться:

Похожие статьи

Telegram Mini App на Next.js: почему SDK падает на сервере
Mini Apps

Telegram Mini App на Next.js: почему SDK падает на сервере

13 мин
@telegram-apps/sdk-react: белый экран и ошибки инициализации Mini App
Mini Apps

@telegram-apps/sdk-react: белый экран и ошибки инициализации Mini App

12 мин
Как создать Telegram Mini App: полная инструкция для разработчика
Mini Apps

Как создать Telegram Mini App: полная инструкция для разработчика

14 мин
Feature IT

Feature IT — платформа по обучению программированию и разработке цифровых продуктов. Мы создаём современные веб-решения для бизнеса и обучаем этому других!

Политика конфиденциальностиПользовательское соглашение

О компании

  • Блог
  • Карьера

Услуги разработки

  • Разработка сайтов под ключ
  • Веб-приложения на React/Next.js
  • Telegram-боты для бизнеса
  • Mini Apps (Telegram, VK)
  • SEO-оптимизированные сайты
  • Автоматизация бизнес-процессов
  • Поддержка и развитие IT-продуктов

Обучение

  • Курс Python с нуля
  • Алгоритмы и структуры данных
  • Паттерны проектирования
  • Подготовка к собеседованиям в IT
  • Практика на реальных проектах

Инструменты

  • Генератор UTM-меток
  • Счётчик символов