В браузере приложение работает. Открываете его в Telegram с телефона — белый экран. Ни ошибки, ни спиннера, ни намёка на то, где сломалось. Консоли нет: внутри Telegram это не вкладка браузера, а встроенный WebView, и F12 там нажать негде.
Дальше начинается угадывание. Разработчик расставляет alert() по коду, пересобирает, перезапускает бота, снова смотрит на белый экран. За вечер так можно не продвинуться никуда.
Проблема почти всегда решается за десять минут, если сначала сделать одну вещь: научиться видеть ошибку. Разберём, как это устроить, а потом пройдём по семи причинам, из-за которых @telegram-apps/sdk-react не стартует.
Всё, что ниже, проверено на v3 пакета: @telegram-apps/sdk-react 3.3.9 поверх @telegram-apps/sdk 3.11.8.
Шаг 0. Сначала увидеть ошибку, потом чинить
Пока вы не видите текст исключения, любые правки остаются лотереей. Есть три способа получить консоль, от самого быстрого к самому надёжному.
Встроенная консоль прямо в приложении. Библиотека вроде eruda или vconsole рисует консоль поверх страницы. Ставится за минуту и работает везде, включая iOS:
// main.tsx — только в деве, в прод такое не уезжает
if (import.meta.env.DEV) {
const eruda = await import("eruda");
eruda.default.init();
}
Отладка по USB. Android: подключите телефон, откройте chrome://inspect/#devices в десктопном Chrome: WebView Telegram появится в списке, и вы получите полноценный DevTools. iOS: Safari → «Разработка» → ваш iPhone, при включённом веб-инспекторе в настройках Safari на телефоне.
Логи самого SDK. У пакета есть собственный режим отладки, который печатает все события между приложением и клиентом Telegram:
import { setDebug } from "@telegram-apps/sdk-react";
setDebug(true);
Это часто ценнее прикладных логов: видно, доходят ли до приложения события от Telegram вообще, или обмен не начался.
Дальше — сами причины, примерно в порядке частоты.
Причина 1. Приложение открыто не из Telegram
Самая частая и самая безобидная. SDK берёт параметры запуска из окружения, которое создаёт клиент Telegram. В обычной вкладке браузера их нет, и любая попытка их достать заканчивается исключением LaunchParamsRetrieveError.
Если это исключение прилетает на верхнем уровне при старте, React не смонтирует ничего, и вы получите ровно белый экран.
Проверять окружение нужно до инициализации:
import { isTMA, init } from "@telegram-apps/sdk-react";
if (!isTMA()) {
// Показать человеческую заглушку вместо пустоты
document.body.innerHTML = "Откройте приложение через Telegram";
} else {
init();
}
Для локальной разработки окружение можно подделать. Тогда приложение запускается в обычном браузере, и большая часть UI отлаживается без телефона:
import { mockTelegramEnv } from "@telegram-apps/sdk-react";
if (import.meta.env.DEV) {
mockTelegramEnv({ launchParams: /* см. типы пакета */ });
}
Два предостережения. Во-первых, точная форма launchParams менялась между мажорными версиями, поэтому берите её из типов установленного пакета, а не из статьи трёхлетней давности. Во-вторых, вызов mockTelegramEnv оборачивает postMessage поверх предыдущей обёртки, поэтому вызывать её несколько раз за жизненный цикл приложения не стоит.
Причина 2. Код написан под старую мажорную версию
Это причина, которую труднее всего заподозрить, потому что код выглядит правильным: он скопирован из статьи или туториала. Просто туториал написан под первую или вторую версию пакета, а установлена третья.
В v3 больше нет ни SDKProvider, ни useInitData, ни хуков вида useBackButton и useMainButton. Импорт несуществующего экспорта, и сборка либо падает, либо отдаёт undefined, который React пытается отрисовать как компонент.
Полный список React-хуков в v3 короткий, их всего шесть:
import {
useSignal, // подписка на сигнал SDK
useLaunchParams, // параметры запуска
useRawLaunchParams, // они же строкой
useRawInitData, // сырая initData
useAndroidDeviceData,
useAndroidDeviceDataFrom,
} from "@telegram-apps/sdk-react";
Всё остальное в v3 — не хуки, а объекты-компоненты и сигналы, которые импортируются напрямую и подписываются через useSignal:
import { backButton, useSignal } from "@telegram-apps/sdk-react";
function BackButtonState() {
// useSignal внутри использует useSyncExternalStore,
// поэтому лишний useState с useEffect тут не нужен
const isVisible = useSignal(backButton.isVisible);
return <span>{isVisible ? "показана" : "скрыта"}</span>;
}
Проверить версию можно одной командой:
npm ls @telegram-apps/sdk-react @telegram-apps/sdk
Если в проекте v3, а код обращается к SDKProvider, вы нашли причину.
Причина 3. Компонент используется до монтирования
В v3 компоненты нужно явно монтировать. Пока mountBackButton() не вызван, обращения к состоянию кнопки бессмысленны, а часть методов бросает исключение.
import {
mountBackButton,
backButton,
isBackButtonMounted,
} from "@telegram-apps/sdk-react";
mountBackButton();
if (isBackButtonMounted()) {
backButton.show();
}
Здесь легко напороться на асинхронность. Часть компонентов монтируется синхронно, часть — нет: у мини-приложения и параметров темы есть отдельные синхронные варианты (mountMiniAppSync, mountThemeParamsSync), а вот mountViewport возвращает промис. Если дёрнуть viewport сразу после вызова, состояние окажется пустым, вёрстка посчитает высоту нулевой, и визуально это тот же белый экран.
Причина 4. Метод не поддерживается версией клиента
Mini Apps развиваются вместе с Bot API, а пользователи сидят на разных версиях Telegram. Метод, который прекрасно работает у вас, на телефоне двухлетней давности не существует — прилетает MethodUnsupportedError.
Никогда не вызывайте свежие возможности вслепую. У компонентов есть сигналы поддержки, а у пакета функция supports:
import { isBackButtonSupported, backButton } from "@telegram-apps/sdk-react";
if (isBackButtonSupported()) {
backButton.show();
} else {
// нарисовать свою кнопку назад внутри приложения
}
Правило простое: любая функция, появившаяся в Bot API позже базовых, должна быть закрыта проверкой поддержки и иметь запасной путь. Иначе приложение падает выборочно — у части аудитории, и в отзывах это выглядит как «у меня не работает» без единого шанса воспроизвести.
Причина 5. SDK дёргается на сервере
Если Mini App живёт внутри Next.js или другого фреймворка с серверным рендерингом, часть кода выполняется там, где нет ни window, ни клиента Telegram. Инициализация на сервере гарантированно бросит исключение.
В пакете для этого есть отдельный флаг и предусмотренный SSR-путь у useSignal: вторым аргументом принимается снимок значения для сервера.
import { isSSR, useSignal, backButton } from "@telegram-apps/sdk-react";
// на сервере вернётся false, гидрация пройдёт без расхождения разметки
const isVisible = useSignal(backButton.isVisible, () => false);
Инициализацию при этом всё равно держат в клиентском коде: в компоненте с "use client" или за динамическим импортом с отключённым SSR.
Причина 6. Приложение не сообщило о готовности
Telegram показывает свою заглушку до тех пор, пока приложение не сообщит, что готово. Если сигнал не отправлен, пользователь видит пустой экран, хотя JavaScript уже отработал и DOM собран.
Вызов должен идти после того, как интерфейс реально готов к показу, а не первой строкой в main.tsx:
import { miniAppReady } from "@telegram-apps/sdk-react";
miniAppReady();
Симптом узнаваемый: в удалённом DevTools дерево элементов на месте, ошибок нет, а на телефоне пусто.
Причина 7. Приложение отрисовано, но его не видно
Последняя категория вообще не про SDK. Контент есть, но лежит за пределами видимой области.
Чаще всего виноваты три вещи. Высота в 100vh, которая внутри WebView считается иначе, чем в браузере, и часть экрана уезжает под интерфейс Telegram. Тёмная тема Telegram поверх тёмного текста — контент есть, но сливается с фоном. И безопасные зоны на телефонах с вырезом.
Пакет умеет отдавать всё это в CSS-переменные, и дальше вёрстка опирается на них, а не на догадки:
import {
bindViewportCssVars,
bindThemeParamsCssVars,
} from "@telegram-apps/sdk-react";
bindViewportCssVars();
bindThemeParamsCssVars();
После этого высота берётся из переменной, которую отдал клиент Telegram, а цвета из темы пользователя.
Чек-лист: от белого экрана к причине
Девять пунктов из десяти проверяются быстрее, чем занимает одна пересборка вслепую.
Заключение
Белый экран в Mini App — не одна поломка, а класс симптомов: приложение не стартовало, стартовало не там, стартовало не в той версии или стартовало, но не показалось. Отличить эти случаи друг от друга без консоли невозможно, поэтому её подключение окупается быстрее любой другой правки.
Если приложение только проектируется, стоит заложить две вещи сразу: заглушку для запуска вне Telegram и проверку поддержки для всего, что новее базового набора методов. Обе стоят десяти строк и снимают половину будущих обращений «у меня белый экран».
Читайте также