Skip to content

RAprogramm/telegram-webapp-sdk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Telegram WebApp SDK

Crates.io docs.rs Downloads MSRV License codecov Hits-of-Code CI REUSE status

Telegram WebApp API Coverage

Wiki

telegram-webapp-sdk provides a type-safe and ergonomic wrapper around the Telegram Web Apps JavaScript API.

Note

Comprehensive Coverage

This project achieves comprehensive test coverage for both native and WASM code:

  • Native code coverage via cargo-llvm-cov
  • WASM code coverage via wasmcov with nightly toolchain

Coverage reports include all modules (leptos, yew, api, webapp, logger, pages, router) ensuring quality across the entire codebase.

For implementation details, see issue #130.

Coverage Graphs

Sunburst

The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.

Sunburst

Grid

Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.

Grid

Icicle

The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.

Icicle

Table of contents

Back to top

Features

  • Comprehensive coverage of Telegram Web App JavaScript APIs.
  • Framework integrations for Yew and Leptos.
  • Optional macros for automatic initialization and routing.
  • Biometric authentication helpers, viewport metrics, and theme utilities in step with the Telegram WebApp API 9.2 feature set.

Back to top

Macros

The macros are available with the macros feature. Enable it in your Cargo.toml:

telegram-webapp-sdk = { version = "0.2.15", features = ["macros"] }

Reduce boilerplate in Telegram Mini Apps using the provided macros:

telegram_page!("/", fn index() {
    // render page
});

telegram_app!(fn main() -> Result<(), wasm_bindgen::JsValue> {
    telegram_router!();
    Ok(())
});

When running outside Telegram in debug builds, telegram_app! loads mock settings from telegram-webapp.toml.

  • Configurable mock Telegram.WebApp for local development and testing.
  • API helpers for user interactions, storage, device sensors and more.

Back to top

Router

The macros feature ships with a minimal in-memory Router that collects pages registered via telegram_page!. The telegram_router! macro builds this router and runs all page handlers:

telegram_page!("/", pub fn index() {});

// Uses the default Router
telegram_router!();

Provide a custom router type to the macro if additional behavior is required:

struct CustomRouter;
impl CustomRouter {
    fn new() -> Self { CustomRouter }
    fn register(self, _path: &str, _handler: fn()) -> Self { self }
    fn start(self) {}
}

telegram_router!(CustomRouter);

Back to top

Installation

Add the crate to your Cargo.toml:

[dependencies]
telegram-webapp-sdk = "0.2"

Enable optional features as needed:

telegram-webapp-sdk = { version = "0.2.15", features = ["macros", "yew", "mock"] }
  • macros — enables telegram_app!, telegram_page!, and telegram_router!.
  • yew — exposes a use_telegram_context hook and a BottomButton component.
  • leptos — integrates the context into the Leptos reactive system.
  • mock — installs a configurable mock Telegram.WebApp for local development.

Back to top

Quick start

Yew

use telegram_webapp_sdk::yew::use_telegram_context;
use yew::prelude::*;

#[function_component(App)]
fn app() -> Html {
    let ctx = use_telegram_context().expect("context");
    if let Some(query_id) = ctx.init_data.query_id.as_deref() {
        // Handle inline query response with `answerWebAppQuery`.
        let _ = query_id;
    }
    html! { <span>{ ctx.init_data.auth_date }</span> }
}

Use BottomButton to control the main button:

use telegram_webapp_sdk::yew::BottomButton;
use yew::prelude::*;

#[function_component(App)]
fn app() -> Html {
    let on_click = Callback::from(|_| {});
    html! { <BottomButton text="Send" color="#000" text_color="#fff" {on_click} /> }
}

Leptos

use leptos::prelude::*;
use telegram_webapp_sdk::leptos::provide_telegram_context;

#[component]
fn App() -> impl IntoView {
    provide_telegram_context().expect("context");
    let ctx = use_context::<telegram_webapp_sdk::core::context::TelegramContext>()
        .expect("context");
    if let Some(query_id) = ctx.init_data.query_id.as_deref() {
        // Handle inline query response with `answerWebAppQuery`.
        let _ = query_id;
    }
    view! { <span>{ ctx.init_data.auth_date }</span> }
}

The SDK also provides a BottomButton component for Leptos to control Telegram bottom buttons:

use leptos::prelude::*;
use telegram_webapp_sdk::leptos::{provide_telegram_context, BottomButton};
use telegram_webapp_sdk::webapp::BottomButton as Btn;

#[component]
fn App() -> impl IntoView {
    provide_telegram_context().expect("context");
    let (text, _set_text) = signal("Send".to_owned());
    view! { <BottomButton button=Btn::Main text /> }
}

Back to top

Mock environment

The mock feature simulates a Telegram.WebApp instance, enabling local development without Telegram:

let config = telegram_webapp_sdk::mock::MockConfig::default();
let ctx = telegram_webapp_sdk::mock::install(config)?;

Back to top

User interactions

Request access to sensitive user data or open the contact interface:

use telegram_webapp_sdk::api::user::{request_contact, request_phone_number, open_contact};
use telegram_webapp_sdk::webapp::TelegramWebApp;

# fn run() -> Result<(), wasm_bindgen::JsValue> {
request_contact()?;
request_phone_number()?;
open_contact()?;

let app = TelegramWebApp::try_instance()?;
app.request_write_access(|granted| {
    let _ = granted;
})?;
# Ok(())
# }

These calls require the user's explicit permission before any information is shared.

Back to top

Keyboard control

Hide the native keyboard when it's no longer required:

use telegram_webapp_sdk::webapp::TelegramWebApp;
# fn run() -> Result<(), wasm_bindgen::JsValue> {
let app = TelegramWebApp::try_instance()?;
app.hide_keyboard()?;
# Ok(())
# }

Back to top

Closing confirmation

Prompt users before the Mini App closes:

use telegram_webapp_sdk::webapp::TelegramWebApp;
# fn run() -> Result<(), wasm_bindgen::JsValue> {
let app = TelegramWebApp::try_instance()?;
app.enable_closing_confirmation()?;
assert!(app.is_closing_confirmation_enabled());
// later
app.disable_closing_confirmation()?;
# Ok(())
# }

Back to top

Invoice payments

Open invoices and react to the final payment status:

use telegram_webapp_sdk::webapp::TelegramWebApp;

# fn run() -> Result<(), wasm_bindgen::JsValue> {
let app = TelegramWebApp::try_instance()?;
let handle = app.on_invoice_closed(|status| {
    let _ = status;
})?;
app.open_invoice("https://invoice", |_status| {})?;
app.off_event(handle)?;
# Ok(())
# }

Back to top

Sharing

Share links, prepared messages, or stories and join voice chats:

use js_sys::Object;
use telegram_webapp_sdk::webapp::TelegramWebApp;

# fn run() -> Result<(), wasm_bindgen::JsValue> {
let app = TelegramWebApp::try_instance()?;
app.share_url("https://example.com", Some("Check this out"))?;
app.join_voice_chat("chat", None)?;
app.share_message("msg-id", |sent| {
    let _ = sent;
})?;
let params = Object::new();
app.share_to_story("https://example.com/image.png", Some(&params.into()))?;
# Ok(())
# }

Back to top

## Settings button

Control the Telegram client's settings button and handle user clicks:

use telegram_webapp_sdk::api::settings_button::{show, hide, on_click, off_click};
use wasm_bindgen::prelude::Closure;

# fn run() -> Result<(), wasm_bindgen::JsValue> {
let cb = Closure::wrap(Box::new(|| {}) as Box<dyn Fn()>);
on_click(&cb)?;
show()?;
hide()?;
off_click(&cb)?;
# Ok(())
# }

Back to top

Cloud storage

Persist small key-value pairs in Telegram's cloud using CloudStorage:

use js_sys::Reflect;
use telegram_webapp_sdk::api::cloud_storage::{get_items, set_items};
use wasm_bindgen_futures::JsFuture;

# async fn run() -> Result<(), wasm_bindgen::JsValue> {
JsFuture::from(set_items(&[("counter", "1")])?).await?;
let obj = JsFuture::from(get_items(&["counter"])?).await?;
let value = Reflect::get(&obj, &"counter".into())?.as_string();
assert_eq!(value, Some("1".into()));
# Ok(())
# }

All functions return a Promise and require the Web App to run inside Telegram.

Back to top

## Home screen

Prompt users to add the app to their home screen and check the current status:

use telegram_webapp_sdk::webapp::TelegramWebApp;
# fn run() -> Result<(), wasm_bindgen::JsValue> {
let app = TelegramWebApp::try_instance()?;
let _shown = app.add_to_home_screen()?;
app.check_home_screen_status(|status| {
    let _ = status;
})?;
# Ok(())
# }

Back to top

Event callbacks

Callback registration methods return an EventHandle for later deregistration.

use telegram_webapp_sdk::webapp::TelegramWebApp;
# fn run() -> Result<(), wasm_bindgen::JsValue> {
let app = TelegramWebApp::try_instance()?;
let handle = app.on_event("my_event", |value| {
    let _ = value;
})?;
app.off_event(handle)?;
# Ok(())
# }

Background events

Some Telegram events may fire while the Mini App is in the background. Register callbacks for these with on_background_event:

use telegram_webapp_sdk::webapp::{BackgroundEvent, TelegramWebApp};

# fn run() -> Result<(), wasm_bindgen::JsValue> {
let app = TelegramWebApp::try_instance()?;
let handle = app.on_background_event(BackgroundEvent::MainButtonClicked, |_| {})?;
app.off_event(handle)?;
# Ok(())
# }

Supported background events:

Event Payload
mainButtonClicked none
backButtonClicked none
settingsButtonClicked none
writeAccessRequested bool granted flag
contactRequested bool shared flag
phoneRequested bool shared flag
invoiceClosed status String
popupClosed object { button_id: Option<String> }
qrTextReceived scanned text String
clipboardTextReceived clipboard text String

Back to top

Appearance

Customize colors and react to theme or safe area updates:

use telegram_webapp_sdk::api::theme::get_theme_params;
use telegram_webapp_sdk::webapp::TelegramWebApp;

# fn run() -> Result<(), wasm_bindgen::JsValue> {
let app = TelegramWebApp::try_instance()?;
app.set_header_color("#0a0a0a")?;
app.set_background_color("#ffffff")?;
app.set_bottom_bar_color("#2481cc")?;

let params = get_theme_params()?;
let _ = params.bg_color;

let theme_handle = app.on_theme_changed(|| {
    let _ = get_theme_params();
})?;
let safe_handle = app.on_safe_area_changed(|| {})?;
let content_handle = app.on_content_safe_area_changed(|| {})?;

app.off_event(theme_handle)?;
app.off_event(safe_handle)?;
app.off_event(content_handle)?;
# Ok(())
# }

Back to top

Viewport

Inspect the Mini App viewport size and subscribe to updates:

use telegram_webapp_sdk::api::viewport::{
    expand_viewport, get_viewport_height, on_viewport_changed,
};
use wasm_bindgen::closure::Closure;

# fn run() -> Result<(), wasm_bindgen::JsValue> {
let _ = get_viewport_height();
let callback = Closure::wrap(Box::new(|| {
    let _ = get_viewport_height();
}) as Box<dyn Fn()>);
on_viewport_changed(&callback);
expand_viewport()?;
callback.forget();
# Ok(())
# }

Back to top

Fullscreen and orientation

Control the Mini App display and screen orientation:

use telegram_webapp_sdk::webapp::TelegramWebApp;
# fn run() -> Result<(), wasm_bindgen::JsValue> {
let app = TelegramWebApp::try_instance()?;
if !app.is_fullscreen() {
    app.request_fullscreen()?;
}
app.lock_orientation("portrait")?;
app.unlock_orientation()?;
app.exit_fullscreen()?;
# Ok(())
# }

Back to top

Haptic feedback

Trigger device vibrations through Telegram's HapticFeedback API:

use telegram_webapp_sdk::api::haptic::{
    impact_occurred, notification_occurred, selection_changed,
    HapticImpactStyle, HapticNotificationType,
};

impact_occurred(HapticImpactStyle::Light)?;
notification_occurred(HapticNotificationType::Success)?;
selection_changed()?;
# Ok::<(), wasm_bindgen::JsValue>(())

Back to top

Device storage

Persist lightweight data on the user's device:

use telegram_webapp_sdk::api::device_storage::{set, get};

# async fn run() -> Result<(), wasm_bindgen::JsValue> {
set("theme", "dark").await?;
let value = get("theme").await?;
# Ok(())
# }

Back to top

Secure storage

Store sensitive data encrypted and restorable:

use telegram_webapp_sdk::api::secure_storage::{set, restore};

# async fn run() -> Result<(), wasm_bindgen::JsValue> {
set("token", "secret").await?;
let _ = restore("token").await?;
# Ok(())
# }

Back to top

## Biometric authentication

Guard privileged actions behind the BiometricManager API:

use telegram_webapp_sdk::api::biometric::{
    authenticate, init, is_biometric_available, request_access,
};

# fn run() -> Result<(), wasm_bindgen::JsValue> {
init()?;
if is_biometric_available()? {
    request_access("auth-key", Some("Unlock the vault"), None)?;
    authenticate("auth-key", None, None)?;
}
# Ok(())
# }

Back to top

Location manager

Retrieve user location and react to related events via Telegram's location manager:

use telegram_webapp_sdk::api::location_manager::{
    init, get_location, open_settings, on_location_requested,
};
use wasm_bindgen::closure::Closure;

init()?;
let _ = get_location();
open_settings()?;

let cb = Closure::wrap(Box::new(|| {}) as Box<dyn Fn()>);
on_location_requested(&cb)?;
cb.forget();
# Ok::<(), wasm_bindgen::JsValue>(())

Back to top

Device sensors

Access motion sensors if the user's device exposes them.

use telegram_webapp_sdk::api::accelerometer::{start, get_acceleration, stop};

start()?;
let reading = get_acceleration();
stop()?;
# Ok::<(), wasm_bindgen::JsValue>(())

Callbacks for sensor lifecycle events are available through on_started, on_changed, on_stopped, and on_failed functions for accelerometer, gyroscope, and device orientation sensors.

Back to top

Init data validation

Retrieving raw initData

Retrieve the raw URL-encoded initData string for server-side authentication. The SDK captures this string during initialization and provides convenient access without requiring JavaScript reflection:

use telegram_webapp_sdk::TelegramWebApp;

# fn run() -> Result<(), Box<dyn std::error::Error>> {
// Get raw initData for backend validation
let raw_init_data = TelegramWebApp::get_raw_init_data()?;

// Send to your backend for signature verification
// POST /auth with body: { "init_data": raw_init_data }
# Ok(())
# }

This eliminates the need for manual Reflect calls and ensures consistency with the parsed data available in the context.

Validating initData

Server-side validation is required. Use the init-data-rs crate for backend validation:

// On your backend server
use init_data_rs::{validate, InitData};

async fn authenticate(init_data_str: &str, bot_token: &str) -> Result<InitData, Box<dyn std::error::Error>> {
    // Validate with optional expiration time (in seconds)
    let init_data: InitData = validate(init_data_str, bot_token, Some(3600))?;
    Ok(init_data)
}

Why server-side only?

  • Bot tokens must never be exposed to client-side code
  • Validation requires secret keys that should remain on the server
  • This follows industry-standard security practices

See the init-data-rs documentation for complete usage examples.

Back to top

API coverage

WebApp API coverage: version 9.2 matches the latest Telegram WebApp API release 9.2. Synced in commit 92abbf7 (recorded on 2025-09-21).

See WEBAPP_API.md for a checklist of supported Telegram WebApp JavaScript API methods and features.

Back to top

Changelog

See CHANGELOG.md for release notes.

Back to top

License

telegram-webapp-sdk is licensed under either of

at your option.

Back to top

Metrics

Metrics

Back to top

About

No description, website, or topics provided.

Resources

Security policy

Stars

Watchers

Forks

Sponsor this project

Packages

No packages published