Особенности разработки и тестирования Telegram mini apps: от инициализации до запуска
В последнее время к нам в компанию поступает всё больше запросов на разработку Telegram mini app. Почему так происходит? Telegram продолжает активно расти, и компании хотят охватить эту аудиторию, предлагая удобные решения внутри мессенджера, где их клиенты проводят значительную часть времени. Об этих и других преимуществах наша команда уже рассказывала в одной из своих статей.
С технической точки зрения, у создания приложения в Телеграм тоже есть свои преимущества: вы можете запустить его прямо в мессенджере, без необходимости разработки отдельных нативных мобильных версий. Это значительно снижает затраты на разработку и поддержку, а также ускоряет выход продукта на рынок.
В этой статье я поделюсь своим опытом разработки приложений в Телеграм и подробно остановлюсь на следующих вопросах:
-
Что представляют из себя Telegram mini apps?
-
Чем они отличаются от других платформ?
-
Какие перспективы развития у Telegram mini apps?
-
Как правильно тестировать Telegram mini apps с учетом особенностей платформы?
-
Как настроить окружение для будущего приложения и инициализировать telegram-apps sdk?
-
Какие нюансы существуют при разработке Telegram mini apps?
Что такое Telegram mini apps и почему они так популярны
Сегодня Telegram mini apps дают возможность открыть любой сайт внутри мессенджера. Технология реализована с помощью специальной браузерной оболочки WebView – системного компонента, который отвечает за интеграцию веб-страниц внутри других приложений и адаптируется под операционную систему устройства.
Telegram mini app может быть любым приложением, которое можно написать с помощью JavaScript или любых инструментов, компилируемых в него. Например, React, Next.js, Angular или даже React Native for Web.
Продукт может быть как отдельным решением, так и частью экосистемы бизнеса. Ранее мы уже рассказывали, как интегрировать готовое React Native-приложение в Telegram с помощью пакета react-native-web.
Перспективы развития Telegram mini apps
Количество пользователей Telegram постепенно приближается к миллиарду. Представьте, что вы можете предоставить им доступ к кроссбраузерному приложению, которое работает на любой операционной системе, будь то iOS, Android, Windows, macOS или Linux. При этом для него не требуется мобильная разработка. Уже сейчас mini app можно добавить на рабочий стол устройства и открывать его в полноэкранном режиме как отдельное приложение.
Функциональность Telegram mini apps и возможность интеграции дополнительных сервисов и инструментов довольно широки: мы можем использовать акселерометр, гироскоп, биометрические параметры, иметь доступ к геолокации, получать push-уведомления и многое другое.
Разумеется, многие вещи можно сделать и с помощью стандартного браузерного API. Но Telegram упрощает взаимодействие с пользователем, авторизацию, проведение платежей, обмен данными, управление ботами, предоставляя все необходимые инструменты для этого. Кроме того, благодаря встроенной безопасности Telegram, mini apps могут быть безопаснее, чем традиционные веб-приложения.
Я уверен, что в будущем Telegram будет развивать формат mini apps в сторону мобильной разработки. Будет появляться всё больше возможностей для доступа к аппаратным возможностям устройств, например к Bluetooth, NFC, Wi-Fi, фонарику, контактам, календарю и так далее.
Работать с Telegram mini apps пользователю также довольно удобно: если на его устройстве уже установлен мессенджер с подключённым аккаунтом, не нужно скачивать ничего дополнительно. Весь функционал уже будет доступен внутри Telegram.
Бизнес, в свою очередь, получает удобное приложение, не прибегая к мобильной разработке, которая, как правило, дороже веб-разработки. Один раз грамотно написав приложение, мы сможем запустить его в браузере, в мобильных и десктопных версиях Telegram, получив больше возможностей и конверсии за меньшую стоимость.
Как правильно тестировать и дебажить Telegram mini app
Немного забегу вперёд и начну с особенностей тестирования Telegram mini apps, так как дальше в части про разработку мы также будем говорить о нём. Главные сложности – невозможность запустить проект так же просто, как в браузере через localhost, а также отсутствие консоли разработчика. Однако эти проблемы легко решаемы.
Как мы уже поняли, Telegram mini app – это сайт, который при указании ссылки на него запускается в окружении Telegram. Поэтому нам нужно передать ссылку, которая будет отображать окружение разработки. Делается это с помощью перенаправления портов.
Есть два наиболее популярных способа сделать это.
В первом случае мы используем стороннюю утилиту ngrok. Но для неё действует лимит на количество подключений в месяц, поэтому такой вариант не очень подходит для разработки.
Во втором случае мы можем воспользоваться VSCode, где нужно открыть наш проект и запустить его в качестве localhost. Далее, открыв терминал, переключаемся на вкладку «Порты», вводим номер порта и получаем ссылку на перенаправленный порт, по которому теперь можно получить доступ к нашему localhost. Именно эту ссылку мы и используем как ссылку на mini app в чат-боте.
Выглядит она примерно так: https://d0etr6gh-3000.euw.devtunnels.ms.
Перенаправленный порт можно сделать:
-
Приватным – никто кроме вас не сможет по нему перейти;
-
Публичным – порт будет доступен всем, если мы хотим поделиться ссылкой на приложение со сторонними пользователями.

Так мы можем запустить наше Telegram mini app и следить за процессом разработки в режиме реального времени, поскольку Hot Reload будет работать так же, как и в браузере.
Но как же проводить дебаг приложение без доступа к консоли разработчика? Для этого познакомимся с Eruda.
Eruda – это мобильная консоль разработчика, которую можно запустить в любом окружении. Достаточно просто нажать на кнопку, зафиксированную в вашем приложении после инициализации этой библиотеки.
Выглядит это примерно так:
Ниже мы будем разбирать, как создать mini app с нуля и как настроить начальный шаблон для инициализации пакета telegram-apps/sdk-react и самого приложения. Там мы увидим, как легко подключить пакет Eruda в наш проект.
Также вы можете прочесть документацию и подключить пакет иным способом. В любом случае это займет всего пару строк строк.
Дополнительно хочу упомянуть ещё про один момент, который относится к тестированию.
Если вы попробуете открыть приложение в браузере, то увидите следующую ошибку:
“Unable to retrieve launch parameters from any known source. Perhaps, you have opened your app outside Telegram?”

Сообщение говорит о том, что mini app невозможно запустить вне окружения Telegram.
Однако мы можем настроить наше приложение таким образом, чтобы его можно было открывать и в браузере. Настоящие данные из Telegram получить не удастся, но мы добавим моковые данные, которые будут имитировать нужное нам окружение.
А теперь перейдем от теории к практике.
Разработка Telegram mini app с нуля
Процесс создания чат-бота внутри Telegram c помощью BotFather мы уже описывали в этой статье.
Поэтому плавно переходим в сам код.
Далее нам нужно инициализировать проект для mini app. Вообще разработчики Telegram постарались и подготовили подборку готовых шаблонов, которые вы можете использовать в зависимости от технологии. Например, вот варианты для Next.js и React.js.
Есть ли смысл использовать Next.js? К сожалению, на данный момент mini apps не способны полностью адаптироваться к SSR, ISR и другим способам рендеринга. Связь с Telegram происходит за счет использования объекта window, получить который невозможно на стороне сервера.
Поэтому, если ваше приложение предполагает идентификацию пользователя или чат-бота, сделать это на сервере вы не сможете и запросы необходимо будет выполнять на клиенте. Однако это не отменяет того, что Next.js обладает дополнительными преимуществами, фичами и оптимизациями в сравнении с React и по-прежнему может быть использована для разработки mini app.
Возвращаемся к коду. Мы взяли один из готовых шаблонов и адаптировали под наши цели, удалив все ненужное и добавив дополнительную логику. Для работы с Telegram mini apps мы использовали пакет @telegram-apps/sdk-react. Расскажем об этом немного подробнее.
У нас есть провайдер, который используется в корневом лайауте для инициализации данного пакета и оборачивает всё приложение.
Выглядит это примерно так:
<html lang={locale} className={`${GeologicaFont.variable}`}>
<body>
<TelegramSDKInitProvider>
<AppInitProvider>
<div className={styles.layout}>{children}</div>
</AppInitProvider>
</TelegramSDKInitProvider>
</body>
</html>
Что вообще здесь происходит и зачем нам два провайдера?
Сначала выполняется вся логика внутри TelegramSDKInitProvider: происходит инициализация пакета, чтобы мы могли получить пользовательские данные и использовать Telegram API в нашем приложении. Мы проверяем окружение и мокаем данные, если находимся в режиме разработки. Это для необходимо для имитации окружения Telegram для запуска приложения в браузере.
В случае, когда мы находимся в продакшен окружении, логичнее вывести ошибку или сообщить пользователю о том, что приложение необходимо запустить внутри Telegram. И в идеале добавить в интерфейс ссылку и QR-код для прямого перехода в мессенджер.
'use client'
import { type PropsWithChildren } from 'react'
import { useClientOnce } from '@/src/hooks/useClientOnce'
import { useTelegramMock } from '@/src/hooks/useTelegramMock'
import { telegramSDKInit } from './init'
export function TelegramSDKInitProvider({ children }: PropsWithChildren) {
if (process.env.NODE_ENV === 'dev') {
useTelegramMock()
}
useClientOnce(() => {
telegramSDKInit(false)
})
return children
}
Внутри хука useTelegramMock используется проверка, предоставленная самим пакетом – isTMA. Так мы можем определить, находимся ли в окружении Telegram. Если нет – мокаем данные, если да – прерываем функцию.
import { isTMA, mockTelegramEnv, parseInitData } from '@telegram-apps/sdk-react'
import { useClientOnce } from './useClientOnce'
export function useTelegramMock(): void {
useClientOnce(() => {
let shouldMock: boolean
const MOCK_KEY = '____mocked'
if (isTMA('simple')) {
shouldMock = !!sessionStorage.getItem(MOCK_KEY)
} else {
shouldMock = true
}
if (!shouldMock) {
return
}
const initDataRaw = new URLSearchParams([
[
'user',
JSON.stringify({
id: 99281932,
first_name: 'Arthur',
last_name: 'Mocked',
username: 'arthurmocked',
language_code: 'en',
is_premium: true,
allows_write_to_pm: true,
photoUrl:
'https://t.me/i/userpic/320/haNXSpRmeucJyo-oXFbNrifxZ-Au0PjvhpJ2l6h4ozcTbTT8yvNKIZABSCLpjtIp.svg',
}),
],
['hash', '89d6079ad6762351f38c6dbbc41bb53048019256a9443988af7a48bcad16ba31'],
['auth_date', '1716922846'],
['start_param', 'debug'],
['chat_type', 'sender'],
['chat_instance', '8428209589180549439'],
]).toString()
mockTelegramEnv({
themeParams: {
accentTextColor: '#6ab2f2',
bgColor: '#17212b',
buttonColor: '#5288c1',
buttonTextColor: '#ffffff',
destructiveTextColor: '#ec3942',
headerBgColor: '#17212b',
hintColor: '#708499',
linkColor: '#6ab3f3',
secondaryBgColor: '#232e3c',
sectionBgColor: '#17212b',
sectionHeaderTextColor: '#6ab3f3',
subtitleTextColor: '#708499',
textColor: '#f5f5f5',
},
initData: parseInitData(initDataRaw),
initDataRaw,
version: '8',
platform: 'tdesktop',
})
sessionStorage.setItem(MOCK_KEY, '1')
console.info(
'
As long as the current environment was not considered as the Telegram-based one, it was mocked. Take a note, that you should not do it in production and current behavior is only specific to the development process. Environment mocking is also applied only in development mode. So, after building the application, you will not see this behavior and related warning, leading to crashing the application outside Telegram.',
)
})
}
Мы можем использовать собственные данные. Например, взятые из вашего Telegram-аккаунта.
Теперь мы можем запустить наше приложение как в Telegram, так и в любом браузере.
Далее выполняется telegramSDKInit, внутри которой уже происходит инициализация sdk-пакета и первоначальная настройка приложения. Например, конфигурация full-screen режима или поведение закрытия приложения при вертикальных свайпах.
Также здесь мы инициализируем мобильную консоль Eruda, которую я уже упомянул в разделе про тестирование. Инициализация происходит лишь один раз – при запуске приложения, что контролируется с помощью хука useClientOnce.
import {
$debug,
backButton,
expandViewport,
init as initSDK,
initData,
miniApp,
swipeBehavior,
themeParams,
viewport,
} from '@telegram-apps/sdk-react'
export function telegramSDKInit(debug: boolean): void {
$debug.set(debug)
initSDK()
expandViewport()
if (backButton.isSupported()) {
backButton.mount()
}
miniApp.mount()
themeParams.mount()
swipeBehavior.mount()
initData.restore()
void viewport
.mount()
.then(() => {
viewport.bindCssVars()
miniApp.bindCssVars()
themeParams.bindCssVars()
viewport.requestFullscreen()
swipeBehavior.disableVertical()
})
.catch((e: unknown) => {
console.error('Something went wrong mounting the viewport', e)
})
debug && import('eruda').then((lib) => lib.default.init()).catch(console.error)
}
Затем выполняется логика AppInitProvider, где мы отображаем состояние загрузки вместо демонстрации контента, так как наше приложение еще не готово к использованию. Здесь мы уже имеем доступ к API Telegram, поэтому получаем необходимые нам данные (например, пользовательские). Далее мы либо записываем ID пользователя в cookies, либо выполняем запрос авторизации, куда передаем необходимые данные из Telegram.
После выполнения всех манипуляций, мы можем отобразить остальную часть приложения.
AppInitProvider может выглядеть так:
'use client'
import { initData, useSignal } from '@telegram-apps/sdk-react'
import { setCookie } from 'cookies-next'
import type { PropsWithChildren } from 'react'
import { useCallback, useEffect, useState } from 'react'
import { postAuthLogin } from '@/src/api/schema'
import TelegramMiniAppLoader from '@/src/components/ui/TelegramMiniAppLoader'
import Title from '@/src/components/ui/Title'
import { AUTH_TOKEN, TELEGRAM_USER_ID } from '@/src/constants'
const AppInitProvider = ({ children }: PropsWithChildren) => {
const [isAppReady, setIsAppReady] = useState(false)
const [error, setError] = useState(null)
const initDataState = useSignal(initData.state)
const initDataStateRaw = useSignal(initData.raw)
const userId = initDataState?.user?.id
const logError = useCallback((message: string) => {
console.error(message)
setError(message)
}, [])
const authenticateUser = useCallback(
async (rawData: string) => {
try {
const { data } = await postAuthLogin({ init_data: rawData })
if (data) {
setCookie(AUTH_TOKEN, data.token)
setIsAppReady(true)
} else {
logError(`Auth token is missing. Response: ${JSON.stringify(data)}`)
}
} catch (err) {
logError(
`Authentication failed: ${err instanceof Error ? err.message : JSON.stringify(err, null, 2)}`,
)
}
},
[logError],
)
const initApp = useCallback(() => {
if (!userId) {
logError(`Telegram user ID is absent. User ID: ${userId}`)
return
}
setCookie(TELEGRAM_USER_ID, userId)
if (!initDataStateRaw) {
logError('Telegram raw data is undefined.')
return
}
authenticateUser(initDataStateRaw)
}, [userId, initDataStateRaw, authenticateUser, logError])
useEffect(() => {
console.log('Initializing app with raw data:', initDataStateRaw)
initApp()
}, [initApp, initDataStateRaw])
if (error) {
return {error}
}
return isAppReady ? <>{children} :
}
export default AppInitProvider
Теперь наше приложение полностью инициализировано и готово к работе.
Особенности разработки Telegram mini app
Рассмотрим ключевые моменты и челленджи, понимание которых необходимо для стабильной работы приложения и удобства пользователей.
Ограничения WebView
Так как Telegram mini apps работают на базе WebView интерфейса, не все функции, которые мы используем в браузерах, будут работать и здесь.
В нашем приложении мы используем iFrame. И если хотим открывать его в полный экран, то сможем использовать requestFullScreen метод. К сожалению, для WebView он не работает. Также некоторые иконки могут отображаться иначе или совсем не отображаться. Особенно, если внутри используются градиенты. Возможны проблемы и при использовании WebSocket, для которого могут понадобиться дополнительные конфигурации. Поэтому важно заложить дополнительное время в разработку на устранение возникающих проблем.

Также я видел на форумах комментарии о том, что использование WebView приводит к снижению производительности. Особенно на устройствах с низкой мощностью и при наличии большого количества сложных анимаций. Некоторые разработчики даже добавляют в приложение возможность отключения анимаций, чтобы пользователи не испытывали проблем с производительностью.
Зависимость от Telegram API
Несмотря на популярность Telegram, у платформы также есть свои баги, которые могут влиять и на ваше приложение. Приведу пример из реальной практики.
Telegram самостоятельно обрабатывает ситуации, когда на мобильных устройствах открывается виртуальная клавиатура и контент автоматически сдвигается таким образом, чтобы клавиатура не перекрывала его. С одной стороны, это очень удобно – разработчикам не нужно писать дополнительную логику, если, к примеру, поле ввода находится внизу экрана. С другой, на момент написания статьи функция все еще некорректно работала на iOS устройствах, тем самым создавая определенные ограничения для пользователей.
Рассмотрим примеры на видео.
Поведение на iOS:
Поведение на Android:
Ограничение при работе с query-параметрами
Если вы попробуете передать стартовые параметры к ссылке mini app, Telegram просто вырежет их. Согласно документации Telegram mini apps, мы можем передать только один параметр, который называется startapp. Выглядит процесс так: https://t.me/botusername/appname?startapp=someParamValue
Но, если вам необходимо передать несколько параметров, можно использовать следующий лайфхак. Для этого в ссылке мы все еще передаем один параметр, однако в него вложено множество других параметров с определенным разделителем. Например, “__”.
https://t.me/botusername/appname?startapp=param1value__param2value__par
am3value
const [param1, param2, param3] = lp.startParam.split("__");
Клиентская природа приложений
Также важно помнить, что mini apps запускаются в основном на стороне клиента. Это накладывает ограничения на использование серверного рендеринга (SSR). Связано это с тем, что для использования Telegram API необходим доступ к window объекту, который доступен только на клиенте.
Специфика платформы Telegram для разработки mini apps
Для создания Telegram mini app нужно понимать специфику самой платформы и особенности работы с WebView. Новичкам рекомендую обратить внимание на следующие нюансы:
-
Telegram WebApp API – взаимодействие с кнопками, событиями, получение данных пользователя. Изучите документацию библиотеки, если вы планируете её использовать для работы с Telegram;
-
Обработка авторизации – правильная работа с initData и проверка подписей. Авторизация здесь выходит на новый уровень, убирая необходимость в постоянном создании и хранении токенов на стороне клиента;
-
Ограничения платформы – особенности рендеринга, навигации и интеграции с фронтенд-фреймворками (например, Next.js);
-
Адаптация интерфейса – работа с тёмной и светлой темами, адаптация под WebView и разработка обходных методов для ограничений данной платформы.
На первый взгляд, это может показаться сложным и трудоёмким, однако изучение нового – важная часть работы программиста. В будущем Telegram наверняка продолжит развивать свой API и расширять поддержку аппаратных возможностей устройств.
Чем раньше вы начнёте работать с этим форматом, тем быстрее научитесь разрабатывать кроссплатформенные решения, глубже разберётесь в интеграции внешних API, а также получите опыт работы с масштабируемыми приложениями для миллиардной аудитории.
На связи была команда dev.family
До скорых встреч!
P.S. По традиции, оставляем ссылки на артефакты:
-
Документация Telegram mini apps: https://core.telegram.org/bots/webapps
-
Документация пакета для взаимодействия с telegram API — telegram-apps/sdk-react https://docs.telegram-mini-apps.com/packages/telegram-apps-sdk-react/2-x
-
Шаблоны от разработчиков Telegram: https://github.com/Telegram-Mini-Apps#templates
-
Шаблон для Telegram mini apps для Next.js: https://github.com/Telegram-Mini-Apps/nextjs-template
-
Шаблон для Telegram mini apps для React.js. https://github.com/Telegram-Mini-Apps/reactjs-template
-
Документация Eruda: https://github.com/liriliri/eruda
-
ngrok: https://ngrok.com/docs/pricing-limits/free-plan-limits/
Автор: dev_family