In this guide, Iāll show you how to build a Telegram Mini App for your bot:
- āļø Create a Next.js project (BFFāāābackend for frontend)
- āļø Deploy your app on Vercel
- š¤ Register your Mini App with BotFather
- š Authorize users through Telegramās Web App initData
- š¬ Enable core features: Message sharing, Styling tips, Back button initialization
- š” Use Telegram Popups
By the end of this guide, youāll have a working Mini App inside the Telegram environment that is production-ready on Vercel.
š§± 1. Project setĀ up
š What YouāllĀ Need
-
Create a bot with @botfather and get your
BOT_TOKEN
- Create your Next.js app
Open your terminal and run the following command. Follow the interactive setup:
npx create-next-app@latest
šļø Using Next.js as a BFF (Backend-for-Frontend) with App Router
- Push your project to GitHub
Go to GitHub, create a new repository, and follow the Quick setup instructions to push your local project to the repo.
- Deploy your app to Vercel
Telegram doesnāt host your websiteāāāyour Mini App must be hosted somewhere publicly accessible.
- Create an account on Vercel
- Link your GitHub account
- Select your newly created repository
- Follow the prompts to deploy your project
š” By this point, your app should be live and hosted on the webāāāready to be linked with your Telegram bot. Also your app will be redeployed on every commit to the remote repository.
šŖ 2. Set Up a MiniĀ App
ā
Method 1: Using /newapp
Command
- Send
/newapp
to BotFather - Choose your bot (e.g.,
@YourAppBot
) - Add a title and description for your Web App
- Provide the URL of your hosted Mini App (e.g., your Vercel app URL)
āļø Method 2: Via BotFatherās Web Interface (Mini App Panel)
- Open BotFatherās Mini App panel
- Go to My Bots and select the bot you want to update
- Navigate to:Ā
Settings ā Mini Apps
- Click on Main App, enable it, and paste in your Mini Appās URL
- Optionally, create a Direct Linkāāāthis is a link that directly opens your Mini App from Telegram( e.g., t.me//your_bot_name/ā¦)
Once set, the
āOpenā button
on the right should launch your Mini App inside Telegram. š
š 3. Authorize a user with the Telegram Mini AppĀ SDK
- Frontend: Get initData from SDK and send it to your backend with API requests.
- Backend: Validate initData using your bot token.
- If valid: The user is authorized! You can trust the user info in initData.
Install the SDK:
npm install @telegram-apps/sdk-react
Since Telegram injects the Telegram
object only in the browser, you must use the SDK inside a client-only componentāāāotherwise, youāll run into SSR issues.
Use next/dynamic
to import your Telegram logic with SSR disabled:
const TelegramInit = dynamic(() => import('../telegram/client/TelegramInit'), {
ssr: false,
});
Save rawInitData to use it for autorization. Send it to your backend to autorize api requests.
import { useRawInitData } from '@telegram-apps/sdk-react';
export default function TelegramInit() {
const rawInitData = useRawInitData();
useEffect(() => {
if (rawInitdata) {
localStorage.setItem('token', rawInitdata);
}
}, [rawInitdata]);
}
const res = await fetch(url, {
method: method,
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${localStorage.getItem('token')}`,
},
body: body ? JSON.stringify(body) : undefined,
});
Also you can get usefull user information from telegram(first_name, last_name, is_bot, photo_url,Ā ā¦)
import { parse } from '@telegram-apps/init-data-node/web';
const initData = localStorage.getItem('token');
if (initData) {
try {
const parsedData = parse(initData);
if (parsedData.user) {
createUserInDB(parsedData.user);
setUser(parsedData.user);
}
} catch (error) {
console.error('Failed to parse user data:', error);
}
Validate initdata on the server using the Telegram Mini Apps SDK:
- token is initdata you get from request headers
- BOT_TOKEN is your bot token from BotFather:
import { isValid } from '@telegram-apps/init-data-node/web';
...
const isAuthorized = isValid(token, process.env.BOT_TOKEN!);
If valid, you can safely use the user info for DB operations.
š Donāt Forget: Set Your BOT_TOKEN
To securely use your botās token in both development and production environments:
š§Ŗ 1. Add it to your local environment
Create aĀ .env.local
file in the root of your Next.js project and add:
BOT_TOKEN=your_telegram_bot_token_here
This keeps your token out of version control and safe from exposure.
āļø 2. Add it to Vercel Environment Variables
To make sure your bot works in production:
- Go to your project dashboard on Vercel
- Open the Settings tab
- Click on Environment Variables
- Add a new variable:
-
Name:
BOT_TOKEN
- Value: your actual bot token
Make sure to
redeploy
your project after saving the variable so itās available at runtime.
š¤ 4. Sharing Messages from MiniĀ App
- Frontend: User clicks āShareā ā send data to /api/share
- Backend: Call savePreparedInlineMessage ā get message ID
- Frontend: Call shareMessage({ id }) with the message ID
Frontend:
import { shareMessage } from '@telegram-apps/sdk-react';
// send info to the backend
const response = await shareTodo(todoId, userId, title, description);
if (response.ok) {
// get event id
const data = await response.json();
if (shareMessage.isAvailable()) {
// share message
await shareMessage(data.id);
}
}
Backend:
On your backend, receive the data and call the Telegram Bot APIās savePreparedInlineMessage method.
const shareMessage = {
user_id: userId,
result: {
type: 'photo',
id: `todo-${todoId}`,
photo_url: 'https://example.com/photo.jpg',
thumbnail_url: 'https://example.com/thumb.jpg',
title: `š ${todoTitle}`,
description: todoDescription || 'A task from my Mini App',
caption: `**${todoTitle}**\n\n${todoDescription || ''}`,
parse_mode: 'Markdown',
},
};
// send reuest to Telegram Bot API
const result = await savePreparedInlineMessage(shareMessage);
return NextResponse.json({ id: result.id });
š± 5. Telegram-Specific Features
š Back Button Behavior
If your Mini App has multiple pages or routing, you might want to enable back navigation. By default, the Telegram back button doesnāt behave like a browser back buttonāāāit needs to be explicitly initialized and controlled.
On pages where you want the back button visible, you should show it; on others, hide it. Otherwise, pressing back will close the Mini App instead of navigating back.
ā Closing Behavior
If you want to remind users to save their data before closing the app, you can enable a confirmation prompt on close. This helps prevent accidental loss of unsaved changes by asking the user for confirmation before exiting.
šØ Mini App Component
To customize your Mini Appāāālike setting the header or background colorsāāāor to use status methods like checking if the Mini App is active, you first need to initialize (mount) the Mini App component. Only after mounting can you safely call these methods.
'use client';
import { useEffect } from 'react';
import {
backButton,
closingBehavior,
init,
miniApp,
useRawInitData,
} from '@telegram-apps/sdk-react';
export default function TelegramInit() {
const rawInitdata = useRawInitData();
useEffect(() => {
try {
init(); // Initialize Telegram SDK
if (backButton.mount.isAvailable()) {
backButton.mount();
backButton.onClick(() => {
if (backButton.isMounted()) {
backButton.hide();
}
window.history.back();
});
}
console.log('TelegramProvider - init()');
if (miniApp.mountSync.isAvailable()) {
miniApp.mountSync();
}
if (closingBehavior.mount.isAvailable()) {
closingBehavior.mount();
closingBehavior.isMounted(); // true
}
if (closingBehavior.enableConfirmation.isAvailable()) {
closingBehavior.enableConfirmation();
closingBehavior.isConfirmationEnabled(); // true
}
} catch (err) {
console.error('Telegram SDK init failed:', err);
const cleanUrl = window.location.origin + window.location.pathname;
window.history.replaceState({}, '', cleanUrl);
}
return () => {
if (backButton.isMounted()) {
backButton.unmount();
}
};
}, []);
š¬ 6. TelegramĀ Popups
Basically you create a popup through popup.show with buttons you need.
Then you wait for buttonId which popup.show returns(which button user clicked) and do accordingly.
import { popup } from '@telegram-apps/sdk-react';
export const showTelegramPopup = async (
title: string,
message: string,
buttonText: string[]
): Promise<boolean> => {
if (popup) {
const promise = popup.show({
title: title,
message: message,
buttons: buttonText.map((text, index) => ({
id: `button_${index}`,
type: 'default',
text: text,
})),
});
// popup.isOpened() -> true
const buttonId = await promise;
if (buttonId === 'button_0') {
return true;
// do something
} else if (buttonId === 'button_1') {
return false;
// do something else
}
// If buttonId is neither 'ok' nor 'cancel', return false by default
return false;
}
// If popup is not available, return false
return false;
};
š Links
- Telegram Mini Apps SDK:
@telegram-apps/sdk-react
and docs - Telegram Bot API: https://api.telegram.org and docs on how to use it.
- Check out final app
š§ FinalĀ Thoughts
Telegram Mini Apps are a powerful way to create native-like experiences directly within Telegram. With the help of Vercel, you can go from idea to live app in no time.
š„ Teaser
Thereās more to exploreāāālike handling payments with Telegram Starsāāābut it didnāt quite fit in this article.
If youād like me to cover it next, please like this post or drop a comment below letting me know!
Your feedback helps me prioritize what to write about next.
This article was originally published on Medium.
Top comments (0)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.