telegram-mini-apps-react

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Telegram Mini Apps with React

基于React开发Telegram Mini Apps

This skill provides comprehensive guidance for building Telegram Mini Apps using React and the
@tma.js/sdk-react
package.
本指南提供了使用React和
@tma.js/sdk-react
包开发Telegram Mini Apps的全面指导。

Overview

概述

Telegram Mini Apps are web applications displayed inside Telegram's WebView. They integrate with Telegram's native UI components (Back Button, Main Button) and have access to user data, theme parameters, and platform-specific features.
Key concepts:
  • Mini Apps are add-ons for Telegram Bots
  • They run inside Telegram's WebView
  • They communicate with Telegram client via events and methods
  • They have access to launch parameters, init data, and theme settings
Telegram Mini Apps是运行在Telegram WebView中的Web应用。它们可以集成Telegram原生UI组件(返回按钮、主按钮),并能访问用户数据、主题参数和平台专属功能。
核心概念:
  • Mini Apps是Telegram机器人的附加功能
  • 运行在Telegram的WebView中
  • 通过事件和方法与Telegram客户端通信
  • 可访问启动参数、初始化数据和主题设置

Quick Start

快速开始

1. Installation

1. 安装

bash
undefined
bash
undefined

For React projects, install the React-specific package

针对React项目,安装React专属包

pnpm i @tma.js/sdk-react
pnpm i @tma.js/sdk-react

DO NOT install both @tma.js/sdk and @tma.js/sdk-react - this causes bugs!

请勿同时安装@tma.js/sdk和@tma.js/sdk-react - 这会导致Bug!


> **Important:** The `@tma.js/sdk-react` package fully re-exports `@tma.js/sdk`, so you don't need to install them separately.

> **重要提示:** `@tma.js/sdk-react`包已完整导出`@tma.js/sdk`的所有内容,因此无需单独安装后者。

2. Create New Project (Optional)

2. 创建新项目(可选)

bash
pnpm dlx @tma.js/create-mini-app@latest
bash
pnpm dlx @tma.js/create-mini-app@latest

or

npx @tma.js/create-mini-app@latest

This CLI scaffolds a complete project with proper configuration.
npx @tma.js/create-mini-app@latest

该CLI工具会快速生成一个配置完善的完整项目。

Project Structure

项目结构

A typical Telegram Mini App React project structure:
src/
├── main.tsx          # Entry point - SDK initialization
├── init.ts           # SDK configuration and component mounting
├── mockEnv.ts        # Development environment mocking
├── App.tsx           # Main React app with routing
├── components/
│   ├── Page.tsx      # Page wrapper with back button handling
│   └── EnvUnsupported.tsx  # Fallback for non-TG environments
├── hooks/
│   └── useDeeplink.ts      # Deep linking handler
└── services/
    └── analytics.ts        # Analytics with user data
典型的Telegram Mini Apps React项目结构如下:
src/
├── main.tsx          # 入口文件 - SDK初始化
├── init.ts           # SDK配置与组件挂载
├── mockEnv.ts        # 开发环境模拟
├── App.tsx           # 带路由的主React应用
├── components/
│   ├── Page.tsx      # 包含返回按钮处理的页面封装组件
│   └── EnvUnsupported.tsx  # 非Telegram环境下的降级UI
├── hooks/
│   └── useDeeplink.ts      # 深度链接处理Hook
└── services/
    └── analytics.ts        # 基于用户数据的统计服务

Core Concepts

核心概念

SDK Initialization

SDK初始化

The SDK must be initialized before using any features. See references/init.md for detailed implementation.
typescript
import { 
  init as initSDK,
  setDebug,
  themeParams,
  miniApp,
  viewport,
  backButton,
  swipeBehavior,
  initData
} from '@tma.js/sdk-react';

export async function init(options: {
  debug: boolean;
  eruda: boolean;
  mockForMacOS: boolean;
}): Promise<void> {
  // Enable debug mode for development
  setDebug(options.debug);
  
  // Initialize the SDK (REQUIRED before using any features)
  initSDK();

  // Mount components you'll use in the app
  backButton.mount.ifAvailable();
  initData.restore();

  // Configure swipe behavior
  if (swipeBehavior.isSupported()) {
    swipeBehavior.mount();
    swipeBehavior.disableVertical();
  }

  // Setup Mini App theming
  if (miniApp.mount.isAvailable()) {
    themeParams.mount();
    miniApp.mount();
    themeParams.bindCssVars();  // Binds theme to CSS variables
  }

  // Configure viewport
  if (viewport.mount.isAvailable()) {
    viewport.mount().then(() => {
      viewport.bindCssVars();
      viewport.requestFullscreen();
    });
  }
}
使用任何功能前必须先初始化SDK。详细实现可参考references/init.md
typescript
import { 
  init as initSDK,
  setDebug,
  themeParams,
  miniApp,
  viewport,
  backButton,
  swipeBehavior,
  initData
} from '@tma.js/sdk-react';

export async function init(options: {
  debug: boolean;
  eruda: boolean;
  mockForMacOS: boolean;
}): Promise<void> {
  // Enable debug mode for development
  setDebug(options.debug);
  
  // Initialize the SDK (REQUIRED before using any features)
  initSDK();

  // Mount components you'll use in the app
  backButton.mount.ifAvailable();
  initData.restore();

  // Configure swipe behavior
  if (swipeBehavior.isSupported()) {
    swipeBehavior.mount();
    swipeBehavior.disableVertical();
  }

  // Setup Mini App theming
  if (miniApp.mount.isAvailable()) {
    themeParams.mount();
    miniApp.mount();
    themeParams.bindCssVars();  // Binds theme to CSS variables
  }

  // Configure viewport
  if (viewport.mount.isAvailable()) {
    viewport.mount().then(() => {
      viewport.bindCssVars();
      viewport.requestFullscreen();
    });
  }
}

Entry Point (main.tsx)

入口文件(main.tsx)

typescript
import { StrictMode } from "react";
import ReactDOM from "react-dom/client";
import { retrieveLaunchParams } from '@tma.js/sdk-react';
import { init } from './init';
import App from "./App";
import { EnvUnsupported } from "./components/EnvUnsupported";

// Mock environment for local development
import './mockEnv';

const root = ReactDOM.createRoot(document.getElementById('root')!);

try {
  const launchParams = retrieveLaunchParams();
  const { tgWebAppPlatform: platform } = launchParams;
  const debug = (launchParams.tgWebAppStartParam || '').includes('debug')
    || import.meta.env.DEV;

  await init({
    debug,
    eruda: debug && ['ios', 'android'].includes(platform),
    mockForMacOS: platform === 'macos',
  }).then(() => {
    root.render(
      <StrictMode>
        <App/>
      </StrictMode>,
    );
  });
} catch (e) {
  // Show fallback UI when not in Telegram
  root.render(<EnvUnsupported/>);
}
typescript
import { StrictMode } from "react";
import ReactDOM from "react-dom/client";
import { retrieveLaunchParams } from '@tma.js/sdk-react';
import { init } from './init';
import App from "./App";
import { EnvUnsupported } from "./components/EnvUnsupported";

// Mock environment for local development
import './mockEnv';

const root = ReactDOM.createRoot(document.getElementById('root')!);

try {
  const launchParams = retrieveLaunchParams();
  const { tgWebAppPlatform: platform } = launchParams;
  const debug = (launchParams.tgWebAppStartParam || '').includes('debug')
    || import.meta.env.DEV;

  await init({
    debug,
    eruda: debug && ['ios', 'android'].includes(platform),
    mockForMacOS: platform === 'macos',
  }).then(() => {
    root.render(
      <StrictMode>
        <App/>
      </StrictMode>,
    );
  });
} catch (e) {
  // Show fallback UI when not in Telegram
  root.render(<EnvUnsupported/>);
}

Using Back Button

返回按钮的使用

The Back Button is a native Telegram UI element that appears in the header.
typescript
import { useEffect } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import { backButton, miniApp } from '@tma.js/sdk-react';

export function Page({ children, back = true }) {
  const navigate = useNavigate();
  const location = useLocation();

  useEffect(() => {
    if (back) {
      backButton.show();
      
      // onClick returns a cleanup function
      return backButton.onClick(() => {
        const isDeeplink = location.state?.fromDeeplink;
        const isFirstPage = !window.history.state || window.history.state.idx === 0;

        if (isDeeplink || isFirstPage) {
          miniApp.close();  // Close the Mini App
        } else {
          navigate(-1);     // Go back in history
        }
      });
    }
    
    backButton.hide();
  }, [back, navigate, location]);

  return <>{children}</>;
}
返回按钮是Telegram原生UI元素,会显示在头部区域。
typescript
import { useEffect } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import { backButton, miniApp } from '@tma.js/sdk-react';

export function Page({ children, back = true }) {
  const navigate = useNavigate();
  const location = useLocation();

  useEffect(() => {
    if (back) {
      backButton.show();
      
      // onClick returns a cleanup function
      return backButton.onClick(() => {
        const isDeeplink = location.state?.fromDeeplink;
        const isFirstPage = !window.history.state || window.history.state.idx === 0;

        if (isDeeplink || isFirstPage) {
          miniApp.close();  // Close the Mini App
        } else {
          navigate(-1);     // Go back in history
        }
      });
    }
    
    backButton.hide();
  }, [back, navigate, location]);

  return <>{children}</>;
}

Using Signals (useSignal Hook)

信号的使用(useSignal Hook)

Signals are reactive values that update automatically. Use
useSignal
to subscribe to them in React:
typescript
import { useEffect } from 'react';
import { backButton, useSignal } from '@tma.js/sdk-react';

function BackButtonStatus() {
  const isVisible = useSignal(backButton.isVisible);

  useEffect(() => {
    console.log('Back button is', isVisible ? 'visible' : 'hidden');
  }, [isVisible]);

  return null;
}
信号是可自动更新的响应式值。在React中使用
useSignal
Hook订阅信号:
typescript
import { useEffect } from 'react';
import { backButton, useSignal } from '@tma.js/sdk-react';

function BackButtonStatus() {
  const isVisible = useSignal(backButton.isVisible);

  useEffect(() => {
    console.log('Back button is', isVisible ? 'visible' : 'hidden');
  }, [isVisible]);

  return null;
}

Getting Init Data (User Info)

获取初始化数据(用户信息)

Init data contains user information and can be used for authentication:
typescript
import { initData } from '@tma.js/sdk-react';

function getUserId(): number | undefined {
  try {
    const user = initData.user();
    return user?.id;
  } catch (e) {
    return undefined;
  }
}

// Get start parameter (for deep linking)
const startParam = initData.startParam();
初始化数据包含用户信息,可用于身份验证:
typescript
import { initData } from '@tma.js/sdk-react';

function getUserId(): number | undefined {
  try {
    const user = initData.user();
    return user?.id;
  } catch (e) {
    return undefined;
  }
}

// Get start parameter (for deep linking)
const startParam = initData.startParam();

Launch Parameters

启动参数

Launch parameters contain platform info, theme, and app data:
typescript
import { retrieveLaunchParams, useLaunchParams } from '@tma.js/sdk-react';

// In component
function Component() {
  const launchParams = useLaunchParams();
  // launchParams.tgWebAppPlatform - 'ios', 'android', 'macos', 'tdesktop', 'web', 'weba'
  // launchParams.tgWebAppVersion - SDK version supported by client
  // launchParams.tgWebAppData - init data
  // launchParams.tgWebAppThemeParams - theme colors
  // launchParams.tgWebAppStartParam - custom start parameter
}

// Outside component
const launchParams = retrieveLaunchParams();
启动参数包含平台信息、主题和应用数据:
typescript
import { retrieveLaunchParams, useLaunchParams } from '@tma.js/sdk-react';

// In component
function Component() {
  const launchParams = useLaunchParams();
  // launchParams.tgWebAppPlatform - 'ios', 'android', 'macos', 'tdesktop', 'web', 'weba'
  // launchParams.tgWebAppVersion - SDK version supported by client
  // launchParams.tgWebAppData - init data
  // launchParams.tgWebAppThemeParams - theme colors
  // launchParams.tgWebAppStartParam - custom start parameter
}

// Outside component
const launchParams = retrieveLaunchParams();

Theming

主题设置

Theme parameters are automatically provided by Telegram. Bind them to CSS variables:
typescript
import { themeParams, miniApp } from '@tma.js/sdk-react';

// During initialization
if (miniApp.mount.isAvailable()) {
  themeParams.mount();
  miniApp.mount();
  themeParams.bindCssVars();  // Creates CSS variables like --tg-theme-bg-color
}
Available CSS variables after binding:
  • --tg-theme-bg-color
  • --tg-theme-text-color
  • --tg-theme-hint-color
  • --tg-theme-link-color
  • --tg-theme-button-color
  • --tg-theme-button-text-color
  • --tg-theme-secondary-bg-color
  • --tg-theme-header-bg-color
  • --tg-theme-accent-text-color
  • --tg-theme-section-bg-color
  • --tg-theme-section-header-text-color
  • --tg-theme-subtitle-text-color
  • --tg-theme-destructive-text-color
主题参数由Telegram自动提供,可将其绑定到CSS变量:
typescript
import { themeParams, miniApp } from '@tma.js/sdk-react';

// During initialization
if (miniApp.mount.isAvailable()) {
  themeParams.mount();
  miniApp.mount();
  themeParams.bindCssVars();  // Creates CSS variables like --tg-theme-bg-color
}
绑定后可用的CSS变量:
  • --tg-theme-bg-color
  • --tg-theme-text-color
  • --tg-theme-hint-color
  • --tg-theme-link-color
  • --tg-theme-button-color
  • --tg-theme-button-text-color
  • --tg-theme-secondary-bg-color
  • --tg-theme-header-bg-color
  • --tg-theme-accent-text-color
  • --tg-theme-section-bg-color
  • --tg-theme-section-header-text-color
  • --tg-theme-subtitle-text-color
  • --tg-theme-destructive-text-color

Viewport and Safe Areas

视口与安全区域

Handle viewport and safe areas for proper layout:
typescript
import { viewport } from '@tma.js/sdk-react';

if (viewport.mount.isAvailable()) {
  viewport.mount().then(() => {
    viewport.bindCssVars();        // Binds viewport dimensions to CSS
    viewport.requestFullscreen();   // Request fullscreen mode
  });
}
Available CSS variables:
css
/* Safe area insets */
padding-top: var(--tg-viewport-safe-area-inset-top, 0);
padding-bottom: var(--tg-viewport-safe-area-inset-bottom, 0);

/* Content safe area (for notch, etc.) */
padding-top: var(--tg-viewport-content-safe-area-inset-top, 0);

/* Viewport dimensions */
height: var(--tg-viewport-height);
width: var(--tg-viewport-width);
Usage in CSS:
css
.header {
  padding-top: max(2rem, calc(var(--tg-viewport-content-safe-area-inset-top, 0) + var(--tg-viewport-safe-area-inset-top, 0)));
}

.footer {
  padding-bottom: calc(1rem + var(--tg-viewport-safe-area-inset-bottom, 0));
}
处理视口和安全区域以实现合理布局:
typescript
import { viewport } from '@tma.js/sdk-react';

if (viewport.mount.isAvailable()) {
  viewport.mount().then(() => {
    viewport.bindCssVars();        // Binds viewport dimensions to CSS
    viewport.requestFullscreen();   // Request fullscreen mode
  });
}
可用的CSS变量:
css
/* Safe area insets */
padding-top: var(--tg-viewport-safe-area-inset-top, 0);
padding-bottom: var(--tg-viewport-safe-area-inset-bottom, 0);

/* Content safe area (for notch, etc.) */
padding-top: var(--tg-viewport-content-safe-area-inset-top, 0);

/* Viewport dimensions */
height: var(--tg-viewport-height);
width: var(--tg-viewport-width);
CSS中的使用示例:
css
.header {
  padding-top: max(2rem, calc(var(--tg-viewport-content-safe-area-inset-top, 0) + var(--tg-viewport-safe-area-inset-top, 0)));
}

.footer {
  padding-bottom: calc(1rem + var(--tg-viewport-safe-area-inset-bottom, 0));
}

Development Environment Mocking

开发环境模拟

For local development outside Telegram, mock the environment. See references/mock-env.md.
typescript
import { emitEvent, isTMA, mockTelegramEnv } from '@tma.js/sdk-react';

if (import.meta.env.DEV) {
  if (!await isTMA('complete')) {
    const themeParams = {
      accent_text_color: '#6ab2f2',
      bg_color: '#17212b',
      button_color: '#5288c1',
      button_text_color: '#ffffff',
      destructive_text_color: '#ec3942',
      header_bg_color: '#17212b',
      hint_color: '#708499',
      link_color: '#6ab3f3',
      secondary_bg_color: '#232e3c',
      section_bg_color: '#17212b',
      section_header_text_color: '#6ab3f3',
      subtitle_text_color: '#708499',
      text_color: '#f5f5f5',
    };

    mockTelegramEnv({
      onEvent(e) {
        if (e.name === 'web_app_request_theme') {
          return emitEvent('theme_changed', { theme_params: themeParams });
        }
        if (e.name === 'web_app_request_viewport') {
          return emitEvent('viewport_changed', {
            height: window.innerHeight,
            width: window.innerWidth,
            is_expanded: true,
            is_state_stable: true,
          });
        }
        if (e.name === 'web_app_request_safe_area') {
          return emitEvent('safe_area_changed', { left: 0, top: 0, right: 0, bottom: 0 });
        }
      },
      launchParams: new URLSearchParams([
        ['tgWebAppThemeParams', JSON.stringify(themeParams)],
        ['tgWebAppData', new URLSearchParams([
          ['auth_date', (Date.now() / 1000 | 0).toString()],
          ['hash', 'mock-hash'],
          ['signature', 'mock-signature'],
          ['user', JSON.stringify({ id: 1, first_name: 'Developer' })],
        ]).toString()],
        ['tgWebAppVersion', '8.4'],
        ['tgWebAppPlatform', 'tdesktop'],
      ]),
    });

    console.info('⚠️ Running in mocked Telegram environment');
  }
}
在Telegram外部进行本地开发时,可模拟Telegram环境。详细内容参考references/mock-env.md
typescript
import { emitEvent, isTMA, mockTelegramEnv } from '@tma.js/sdk-react';

if (import.meta.env.DEV) {
  if (!await isTMA('complete')) {
    const themeParams = {
      accent_text_color: '#6ab2f2',
      bg_color: '#17212b',
      button_color: '#5288c1',
      button_text_color: '#ffffff',
      destructive_text_color: '#ec3942',
      header_bg_color: '#17212b',
      hint_color: '#708499',
      link_color: '#6ab3f3',
      secondary_bg_color: '#232e3c',
      section_bg_color: '#17212b',
      section_header_text_color: '#6ab3f3',
      subtitle_text_color: '#708499',
      text_color: '#f5f5f5',
    };

    mockTelegramEnv({
      onEvent(e) {
        if (e.name === 'web_app_request_theme') {
          return emitEvent('theme_changed', { theme_params: themeParams });
        }
        if (e.name === 'web_app_request_viewport') {
          return emitEvent('viewport_changed', {
            height: window.innerHeight,
            width: window.innerWidth,
            is_expanded: true,
            is_state_stable: true,
          });
        }
        if (e.name === 'web_app_request_safe_area') {
          return emitEvent('safe_area_changed', { left: 0, top: 0, right: 0, bottom: 0 });
        }
      },
      launchParams: new URLSearchParams([
        ['tgWebAppThemeParams', JSON.stringify(themeParams)],
        ['tgWebAppData', new URLSearchParams([
          ['auth_date', (Date.now() / 1000 | 0).toString()],
          ['hash', 'mock-hash'],
          ['signature', 'mock-signature'],
          ['user', JSON.stringify({ id: 1, first_name: 'Developer' })],
        ]).toString()],
        ['tgWebAppVersion', '8.4'],
        ['tgWebAppPlatform', 'tdesktop'],
      ]),
    });

    console.info('⚠️ Running in mocked Telegram environment');
  }
}

Deep Linking

深度链接

Handle start parameters for deep linking. See references/deeplink.md.
typescript
import { useEffect, useRef } from "react";
import { useNavigate } from "react-router-dom";
import { initData } from "@tma.js/sdk-react";

export function useDeeplink() {
  const navigate = useNavigate();
  const processedRef = useRef(false);

  useEffect(() => {
    if (processedRef.current) return;

    const startParam = initData.startParam();
    if (!startParam) return;

    processedRef.current = true;

    try {
      // startParam is base64url encoded
      const base64 = startParam.replace(/-/g, '+').replace(/_/g, '/');
      const decoded = atob(base64);
      const params = new URLSearchParams(decoded);
      
      const route = params.get('route');
      if (route) {
        navigate(route, { replace: true, state: { fromDeeplink: true } });
      }
    } catch (e) {
      console.error("Failed to parse startParam:", e);
    }
  }, [navigate]);
}
处理启动参数以实现深度链接。详细内容参考references/deeplink.md
typescript
import { useEffect, useRef } from "react";
import { useNavigate } from "react-router-dom";
import { initData } from "@tma.js/sdk-react";

export function useDeeplink() {
  const navigate = useNavigate();
  const processedRef = useRef(false);

  useEffect(() => {
    if (processedRef.current) return;

    const startParam = initData.startParam();
    if (!startParam) return;

    processedRef.current = true;

    try {
      // startParam is base64url encoded
      const base64 = startParam.replace(/-/g, '+').replace(/_/g, '/');
      const decoded = atob(base64);
      const params = new URLSearchParams(decoded);
      
      const route = params.get('route');
      if (route) {
        navigate(route, { replace: true, state: { fromDeeplink: true } });
      }
    } catch (e) {
      console.error("Failed to parse startParam:", e);
    }
  }, [navigate]);
}

Best Practices

最佳实践

1. Always Check Availability

1. 始终检查功能可用性

Before using any method, check if it's available:
typescript
import { backButton } from '@tma.js/sdk-react';

// Option 1: Check before calling
if (backButton.show.isAvailable()) {
  backButton.show();
}

// Option 2: Call only if available (safer, no-op if unavailable)
backButton.show.ifAvailable();

// Option 3: Mount only if available
backButton.mount.ifAvailable();
使用任何方法前,先检查其是否可用:
typescript
import { backButton } from '@tma.js/sdk-react';

// Option 1: Check before calling
if (backButton.show.isAvailable()) {
  backButton.show();
}

// Option 2: Call only if available (safer, no-op if unavailable)
backButton.show.ifAvailable();

// Option 3: Mount only if available
backButton.mount.ifAvailable();

2. Mount Components Before Use

2. 使用前先挂载组件

Components must be mounted before their methods can be used:
typescript
// ❌ Wrong - will throw error
backButton.show();

// ✅ Correct
backButton.mount();
backButton.show();
组件必须先挂载才能使用其方法:
typescript
// ❌ Wrong - will throw error
backButton.show();

// ✅ Correct
backButton.mount();
backButton.show();

3. Handle macOS Bugs

3. 处理macOS平台的Bug

Telegram for macOS has known issues:
typescript
if (platform === 'macos') {
  mockTelegramEnv({
    onEvent(event, next) {
      if (event.name === 'web_app_request_theme') {
        const tp = themeParams.state() || retrieveLaunchParams().tgWebAppThemeParams;
        return emitEvent('theme_changed', { theme_params: tp });
      }
      if (event.name === 'web_app_request_safe_area') {
        return emitEvent('safe_area_changed', { left: 0, top: 0, right: 0, bottom: 0 });
      }
      next();
    },
  });
}
Telegram for macOS存在一些已知问题:
typescript
if (platform === 'macos') {
  mockTelegramEnv({
    onEvent(event, next) {
      if (event.name === 'web_app_request_theme') {
        const tp = themeParams.state() || retrieveLaunchParams().tgWebAppThemeParams;
        return emitEvent('theme_changed', { theme_params: tp });
      }
      if (event.name === 'web_app_request_safe_area') {
        return emitEvent('safe_area_changed', { left: 0, top: 0, right: 0, bottom: 0 });
      }
      next();
    },
  });
}

4. Don't Install Duplicate SDKs

4. 不要重复安装SDK

Never install both
@tma.js/sdk
and
@tma.js/sdk-react
:
json
// ❌ Wrong - causes bugs
{
  "dependencies": {
    "@tma.js/sdk": "^3.0.0",
    "@tma.js/sdk-react": "^3.0.8"
  }
}

// ✅ Correct - only the React package
{
  "dependencies": {
    "@tma.js/sdk-react": "^3.0.8"
  }
}
切勿同时安装
@tma.js/sdk
@tma.js/sdk-react
json
// ❌ Wrong - causes bugs
{
  "dependencies": {
    "@tma.js/sdk": "^3.0.0",
    "@tma.js/sdk-react": "^3.0.8"
  }
}

// ✅ Correct - only the React package
{
  "dependencies": {
    "@tma.js/sdk-react": "^3.0.8"
  }
}

5. Disable Swipe When Needed

5. 必要时禁用滑动操作

Prevent accidental navigation:
typescript
if (swipeBehavior.isSupported()) {
  swipeBehavior.mount();
  swipeBehavior.disableVertical();  // Prevents swipe-to-close
}
防止意外导航:
typescript
if (swipeBehavior.isSupported()) {
  swipeBehavior.mount();
  swipeBehavior.disableVertical();  // Prevents swipe-to-close
}

Sending Init Data to Server

将初始化数据发送到服务端

For authentication, send init data to your server:
typescript
import { retrieveRawInitData } from '@tma.js/sdk-react';

const initDataRaw = retrieveRawInitData();

fetch('https://api.example.com/auth', {
  method: 'POST',
  headers: {
    Authorization: `tma ${initDataRaw}`,
  },
});
Server-side validation:
  • Use
    @tma.js/init-data-node
    for Node.js
  • Validate the hash using your bot token
  • Never trust init data without validation
用于身份验证时,可将初始化数据发送到服务端:
typescript
import { retrieveRawInitData } from '@tma.js/sdk-react';

const initDataRaw = retrieveRawInitData();

fetch('https://api.example.com/auth', {
  method: 'POST',
  headers: {
    Authorization: `tma ${initDataRaw}`,
  },
});
服务端验证:
  • Node.js环境可使用
    @tma.js/init-data-node
  • 使用机器人令牌验证哈希值
  • 未经过验证的初始化数据绝不可信

Supported Platforms

支持的平台

Mini Apps work on:
  • android - Telegram for Android
  • ios - Telegram for iOS
  • macos - Telegram for macOS (has some bugs)
  • tdesktop - Telegram Desktop
  • weba - Telegram Web A
  • web - Telegram Web K
Mini Apps可在以下平台运行:
  • android - Telegram for Android
  • ios - Telegram for iOS
  • macos - Telegram for macOS(存在部分Bug)
  • tdesktop - Telegram Desktop
  • weba - Telegram Web A
  • web - Telegram Web K

Common Issues

常见问题

SDK not initialized error

SDK未初始化错误

Make sure to call
init()
before using any SDK features.
确保在使用任何SDK功能前调用
init()
方法。

Component not mounted error

组件未挂载错误

Mount the component before calling its methods:
typescript
backButton.mount();
backButton.show();
调用组件方法前先挂载组件:
typescript
backButton.mount();
backButton.show();

Method not available error

方法不可用错误

Check availability before calling:
typescript
if (backButton.show.isAvailable()) {
  backButton.show();
}
调用前先检查可用性:
typescript
if (backButton.show.isAvailable()) {
  backButton.show();
}

App crashes outside Telegram

在Telegram外部运行时应用崩溃

Use environment mocking during development and provide a fallback UI.
开发时使用环境模拟,并提供降级UI。

Additional Resources

额外资源

See Also

相关链接

  • references/init.md - Full initialization example
  • references/mock-env.md - Environment mocking
  • references/deeplink.md - Deep linking implementation
  • examples/page-component.tsx - Page wrapper with back button
  • references/init.md - 完整初始化示例
  • references/mock-env.md - 环境模拟指南
  • references/deeplink.md - 深度链接实现
  • examples/page-component.tsx - 带返回按钮的页面封装组件