-
-
Notifications
You must be signed in to change notification settings - Fork 0
2. Installation & Setup
Professional guide to setting up a production-ready Telegram Mini App with telegram-webapp-sdk.
Install Rust 1.90.0 or later (MSRV for telegram-webapp-sdk 0.3.0):
# Install Rust via rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Verify installation
rustc --version # Should be 1.90.0 or later
cargo --versionReference: Official Rust Installation Guide
Add the wasm32-unknown-unknown target for browser-based WebAssembly:
rustup target add wasm32-unknown-unknownThis target is required for all Rust WASM applications. Verify installation:
rustup target list --installed | grep wasm32Trunk is the official recommended bundler for Yew and Rust WASM applications:
cargo install --locked trunkNote: Installation compiles from source and may take 5-10 minutes.
Verify installation:
trunk --versionReference: Trunk Documentation
Create a bot via @BotFather:
- Send
/newbotto BotFather - Follow the prompts to choose a name and username
- Save your bot token (format:
110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw) - Keep the token secure - never commit it to version control
Reference: Telegram Bot API Documentation
# Create new Cargo project
cargo new --bin my-telegram-app
cd my-telegram-app
# Create additional directories
mkdir -p src/components
mkdir -p src/pages
mkdir -p assetsStandard Yew Project Structure:
my-telegram-app/
├── Cargo.toml # Project manifest
├── Trunk.toml # Trunk configuration (optional)
├── index.html # HTML entry point
├── assets/ # Static assets
│ ├── styles.css
│ └── images/
├── src/
│ ├── main.rs # Application entry point
│ ├── components/ # Reusable components
│ │ └── mod.rs
│ ├── pages/ # Page components
│ │ └── mod.rs
│ └── lib.rs # Optional library exports
└── dist/ # Build output (generated)
Reference: Yew Project Structure
Edit Cargo.toml:
[package]
name = "my-telegram-app"
version = "0.1.0"
edition = "2021"
rust-version = "1.90"
# Optimize for WebAssembly
[profile.release]
opt-level = "z" # Optimize for size
lto = true # Enable link-time optimization
codegen-units = 1 # Better optimization (slower compile)
strip = true # Strip symbols from binary
panic = "abort" # Smaller code without unwinding
[dependencies]
# Telegram WebApp SDK
telegram-webapp-sdk = { version = "0.3", features = ["yew", "macros"] }
# Yew Framework (CSR = Client-Side Rendering)
yew = { version = "0.21", features = ["csr"] }
# Utilities
wasm-bindgen = "0.2"
web-sys = "0.3"
gloo = "0.11" # Utility library for web APIs
serde = { version = "1", features = ["derive"] }
serde_json = "1"
# Optional: For HTTP requests
reqwasm = { version = "0.5", optional = true }
# Optional: For state management
yewdux = { version = "0.10", optional = true }
[dev-dependencies]
wasm-bindgen-test = "0.3"
[features]
default = []
http = ["reqwasm"]
state = ["yewdux"]Reference: Yew Dependencies
Create Trunk.toml in project root:
[build]
# Target directory for build output
target = "dist"
# Base URL for assets (change for production)
public_url = "/"
# Release optimizations
[build.release]
minify = "on_release"
# Serve configuration
[serve]
port = 8080
address = "127.0.0.1"
# Enable automatic page reload on changes
open = false
# Proxy API requests (optional)
# [[proxy]]
# backend = "http://localhost:3000/api/"
# Build hooks
[[hooks]]
stage = "pre_build"
command = "echo"
command_arguments = ["Starting build..."]
[[hooks]]
stage = "post_build"
command = "echo"
command_arguments = ["Build complete!"]
# wasm-opt optimization (requires binaryen installed)
[[hooks]]
stage = "post_build"
command = "sh"
command_arguments = [
"-c",
"if command -v wasm-opt >/dev/null 2>&1; then wasm-opt -Oz -o dist/*.wasm dist/*.wasm; fi"
]Reference: Trunk Configuration
Create index.html in project root:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Telegram Mini App" />
<title>My Telegram App</title>
<!-- CRITICAL: Telegram WebApp SDK must load before WASM -->
<script src="https://telegram.org/js/telegram-web-app.js"></script>
<!-- Trunk will inject WASM bundle here -->
<link data-trunk rel="rust" data-wasm-opt="z" />
<!-- Optional: Load CSS -->
<link data-trunk rel="css" href="assets/styles.css" />
<!-- Optional: Preload assets -->
<!-- <link data-trunk rel="copy-dir" href="assets/images" /> -->
</head>
<body>
<!-- Yew will mount here -->
<noscript>
<p>This application requires JavaScript to run.</p>
</noscript>
</body>
</html>Key Points:
-
data-trunk rel="rust"- Tells Trunk to compile and inject WASM -
data-wasm-opt="z"- Optimize WASM for size - Telegram script loads before WASM bundle
Reference: Trunk Assets
use telegram_webapp_sdk::{telegram_app, telegram_router};
use yew::prelude::*;
mod components;
mod pages;
use pages::{Home, Profile};
/// Main application component with routing
#[telegram_app(auto_init)]
#[function_component(App)]
fn app() -> Html {
telegram_router! {
"/" => Home,
"/profile" => Profile,
}
}
/// Application entry point
fn main() {
// Initialize panic hook for better error messages
#[cfg(debug_assertions)]
console_error_panic_hook::set_once();
// Render the application
yew::Renderer::<App>::new().render();
}// Export all components
pub mod header;
pub mod footer;
pub use header::Header;
pub use footer::Footer;use telegram_webapp_sdk::TelegramWebApp;
use yew::prelude::*;
#[function_component(Header)]
pub fn header() -> Html {
let webapp = TelegramWebApp::new();
let theme = webapp.theme_params();
let bg_color = theme.header_bg_color
.as_ref()
.unwrap_or(&"#000000".to_string())
.clone();
html! {
<header style={format!("background-color: {}", bg_color)}>
<h1>{ "My Telegram App" }</h1>
</header>
}
}// Export all pages
pub mod home;
pub mod profile;
pub use home::Home;
pub use profile::Profile;use telegram_webapp_sdk::{telegram_page, TelegramWebApp};
use yew::prelude::*;
#[telegram_page(path = "/")]
#[function_component(Home)]
pub fn home() -> Html {
let webapp = TelegramWebApp::new();
// Get user information
let user_name = webapp
.init_data_unsafe()
.user
.as_ref()
.map(|u| u.first_name.clone())
.unwrap_or_else(|| "Guest".to_string());
// Setup main button
use_effect_with((), move |_| {
webapp.main_button().set_text("Continue");
webapp.main_button().show();
webapp.main_button().on_click(Callback::from(|_| {
// Handle button click
}));
|| ()
});
html! {
<div class="page">
<h1>{ format!("Hello, {}!", user_name) }</h1>
<p>{ "Welcome to your Telegram Mini App" }</p>
</div>
}
}use telegram_webapp_sdk::{telegram_page, TelegramWebApp};
use yew::prelude::*;
#[telegram_page(path = "/profile")]
#[function_component(Profile)]
pub fn profile() -> Html {
let webapp = TelegramWebApp::new();
let user = webapp.init_data_unsafe().user.clone();
html! {
<div class="page">
if let Some(user) = user {
<div>
<h1>{ "Profile" }</h1>
<p>{ format!("ID: {}", user.id) }</p>
<p>{ format!("Name: {}", user.first_name) }</p>
if let Some(username) = user.username {
<p>{ format!("Username: @{}", username) }</p>
}
</div>
} else {
<p>{ "No user data available" }</p>
}
</div>
}
}* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background-color: var(--tg-theme-bg-color, #ffffff);
color: var(--tg-theme-text-color, #000000);
}
.page {
padding: 20px;
max-width: 600px;
margin: 0 auto;
}
header {
padding: 16px;
text-align: center;
border-bottom: 1px solid var(--tg-theme-hint-color, #cccccc);
}
button {
background-color: var(--tg-theme-button-color, #0088cc);
color: var(--tg-theme-button-text-color, #ffffff);
border: none;
border-radius: 8px;
padding: 12px 24px;
font-size: 16px;
cursor: pointer;
}
button:hover {
opacity: 0.9;
}For testing without deploying, add mock feature:
[dependencies]
telegram-webapp-sdk = { version = "0.3", features = ["yew", "macros", "mock"] }Initialize mock in main.rs:
#[cfg(feature = "mock")]
fn init_mock_env() {
use telegram_webapp_sdk::mock::{init_mock, MockConfig, MockUser};
init_mock(MockConfig {
user: Some(MockUser {
id: 12345,
first_name: "Test User".into(),
last_name: Some("Developer".into()),
username: Some("testdev".into()),
language_code: Some("en".into()),
is_premium: Some(false),
..Default::default()
}),
platform: "web",
version: "7.0",
..Default::default()
});
}
fn main() {
#[cfg(feature = "mock")]
init_mock_env();
yew::Renderer::<App>::new().render();
}# Start with hot reload
trunk serve
# Or with custom port
trunk serve --port 3000
# With release optimizations (slower build, faster runtime)
trunk serve --releaseReference: Trunk Serve Command
# Build optimized bundle
trunk build --release
# Build with custom public URL
trunk build --release --public-url "https://your-domain.com/app/"
# Output will be in dist/ directory
ls -lh dist/Telegram requires HTTPS. Use tunneling tools:
# Install: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/
# Terminal 1: Start dev server
trunk serve
# Terminal 2: Create tunnel
cloudflared tunnel --url http://localhost:8080You'll receive a URL like: https://random-words.trycloudflare.com
# Install: https://ngrok.com/download
# Terminal 1: Start dev server
trunk serve
# Terminal 2: Create tunnel
ngrok http 8080You'll receive a URL like: https://abc123.ngrok-free.app
1. Open @BotFather in Telegram
2. Send: /mybots
3. Select your bot
4. Bot Settings → Menu Button → Edit menu button
5. Choose "Edit menu button URL"
6. Enter your URL (tunnel or production)
7. Done
1. Open your bot in Telegram
2. Click the menu button (bottom-left)
3. Your WebApp opens in Telegram
In Trunk.toml:
[build]
# Enable source maps in debug builds
dist = "dist"
[build.debug]
minify = "never"In Trunk.toml:
[[proxy]]
# Proxy /api requests to backend
backend = "http://localhost:3000/api/"Then in your app:
// Requests to /api/users will proxy to http://localhost:3000/api/users
reqwasm::http::Request::get("/api/users")
.send()
.await?;Reference: Trunk Proxy Configuration
Install wasm-opt:
# macOS
brew install binaryen
# Ubuntu/Debian
sudo apt-get install binaryen
# Windows (scoop)
scoop install binaryenTrunk will automatically use it during release builds.
Cause: Telegram script not loaded before WASM
Solution: Ensure index.html has:
<script src="https://telegram.org/js/telegram-web-app.js"></script>Before:
<link data-trunk rel="rust" />Cause: CLI version differs from library version
Solution:
# Check versions
grep wasm-bindgen Cargo.lock | head -1
# Install matching CLI
cargo install wasm-bindgen-cli --version 0.2.XXReference: wasm-bindgen Docs
Cause: Incorrect public URL or CORS issues
Solution:
# Rebuild with correct URL
trunk build --release --public-url "https://your-actual-domain.com/"
# Check server CORS headers
curl -I https://your-domain.com/app.wasmSolutions:
- Enable all optimizations in
Cargo.toml - Use
wasm-opt -Oz - Remove unused dependencies
- Use
cargo-bloatto find large dependencies:
cargo install cargo-bloat
cargo bloat --release --target wasm32-unknown-unknownReference: Rust WASM Optimization
Your project is now set up. Learn the core concepts: