Crates.io | telegram-webapp-sdk |
lib.rs | telegram-webapp-sdk |
version | 0.2.10 |
created_at | 2025-09-12 03:09:37.16022+00 |
updated_at | 2025-09-22 07:03:23.040372+00 |
description | Telegram WebApp SDK for Rust |
homepage | |
repository | https://github.com/RAprogramm/telegram-webapp-sdk |
max_upload_size | |
id | 1834998 |
size | 493,947 |
telegram-webapp-sdk
provides a type-safe and ergonomic wrapper around the Telegram Web Apps JavaScript API.
The macros are available with the macros
feature. Enable it in your Cargo.toml
:
telegram-webapp-sdk = { version = "0.2.10", 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
.
Telegram.WebApp
for local development and testing.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);
Add the crate to your Cargo.toml
:
[dependencies]
telegram-webapp-sdk = "0.2"
Enable optional features as needed:
telegram-webapp-sdk = { version = "0.2.10", 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.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} /> }
}
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 /> }
}
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)?;
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.
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(())
# }
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(())
# }
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(())
# }
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(¶ms.into()))?;
# Ok(())
# }
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(())
# }
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.
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(())
# }
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(())
# }
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 |
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(())
# }
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(())
# }
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(())
# }
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>(())
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(())
# }
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(())
# }
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(())
# }
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>(())
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.
Validate the integrity of the Telegram.WebApp.initData
payload on the server.
The validate_init_data
module is re-exported at the crate root and can be
used directly or through the TelegramWebApp::validate_init_data
helper:
use telegram_webapp_sdk::{
validate_init_data::ValidationKey,
TelegramWebApp
};
let bot_token = "123456:ABC";
let query = "user=alice&auth_date=1&hash=48f4c0e9d3dd46a5734bf2c5d4df9f4ec52a3cd612f6482a7d2c68e84e702ee2";
TelegramWebApp::validate_init_data(query, ValidationKey::BotToken(bot_token))?;
// For Ed25519-signed data
# use ed25519_dalek::{Signer, SigningKey};
# let sk = SigningKey::from_bytes(&[1u8;32]);
# let pk = sk.verifying_key();
# let sig = sk.sign(b"a=1\nb=2");
# let init_data = format!("a=1&b=2&signature={}", base64::encode(sig.to_bytes()));
TelegramWebApp::validate_init_data(
&init_data,
ValidationKey::Ed25519PublicKey(pk.as_bytes())
)?;
# Ok::<(), Box<dyn std::error::Error>>(())
WebApp API coverage: version 9.2
matches the latest Telegram WebApp API release 9.2
. Synced in commit 92abbf7 (recorded on 2025-09-20).
See WEBAPP_API.md for a checklist of supported Telegram WebApp JavaScript API methods and features.
See CHANGELOG.md for release notes.
telegram-webapp-sdk
is licensed under either of
at your option.