Blog/How to Build a Simple Telegram Mini App

How to Build a Simple Telegram Mini App

2025-10-12Guides
How to Build a Simple Telegram Mini App
How to Build a Simple Telegram Mini App with Next.js and MongoDB?

🧠 How to Build a Simple Telegram Mini App with Next.js and MongoDB


Telegram Mini Apps are rapidly growing in popularity - they let developers build interactive web experiences that live inside Telegram itself. You can sell, gamify, or even monetize your Mini Apps through ad platforms like AdRadar. In this tutorial series, we’ll build a simple “Click Counter” Telegram Mini App step-by-step using Next.js and MongoDB. It’ll be minimal, clean, and fully functional - a great starting point for your own ideas.


🧩 What We’re Building

Our Mini App will:

  1. • Authenticate users via Telegram WebApp SDK
  2. • Display a personalized counter
  3. • Let users click a button to increase their count
  4. • Save that data to MongoDB
  5. • Look clean and responsive (using TailwindCSS)


Here’s the structure we’re aiming for:

click-counter-miniapp/
├── app/
│ ├── page.tsx
│ ├── api/
│ │ ├── clicks/route.ts
│ └── layout.tsx
├── lib/
│ ├── db.ts
│ └── models/
│ └── User.ts
├── package.json
├── .env.local
└── README.md


⚙️ Step 1: Initialize the Project

We’ll start by creating a new Next.js project and adding the required dependencies.

Run the following commands:


npx create-next-app@latest click-counter-miniapp
cd click-counter-miniapp
npm install mongoose
npm install telegram-web-app --save
npm install tailwindcss postcss autoprefixer
npx tailwindcss init -p


These commands:

  1. • Create a new Next.js app
  2. • Install Mongoose for MongoDB connections
  3. • Install Telegram WebApp SDK to integrate with Telegram
  4. • Add TailwindCSS for styling


⚙️ Step 2: Setting up TailwindCSS and MongoDB

Now that our base project is ready, let’s make it look nice and start handling data.


🎨 Configuring TailwindCSS

Open your tailwind.config.js and replace its content with:


/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./app/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
};


Then, edit your ./app/globals.css file and make sure it includes:


@tailwind base;
@tailwind components;
@tailwind utilities;


That’s it - Tailwind is ready.

We’ll use it later for clean UI with just a few classes.


🧵 Connecting to MongoDB

Now let’s connect to our database. Inside the lib folder, create a new file named db.ts:


import mongoose from "mongoose";

const MONGODB_URI = process.env.MONGODB_URI || "";

if (!MONGODB_URI) {
throw new Error("Please define the MONGODB_URI environment variable in .env.local");
}

let cached = global.mongoose;

if (!cached) {
cached = global.mongoose = { conn: null, promise: null };
}

export async function connectDB() {
if (cached.conn) return cached.conn;

if (!cached.promise) {
cached.promise = mongoose.connect(MONGODB_URI).then((mongoose) => mongoose);
}
cached.conn = await cached.promise;
return cached.conn;
}


This helper ensures we reuse the same database connection (important in serverless environments like Vercel).

Then, create .env.local and add:


MONGODB_URI=mongodb+srv://<your-user>:<your-password>@cluster.mongodb.net/miniapp


🧍 Defining the User Model

In lib/models/User.ts add:


import mongoose, { Schema, models } from "mongoose";

const UserSchema = new Schema({
telegramId: { type: String, required: true, unique: true },
clicks: { type: Number, default: 0 },
});

export const User = models.User || mongoose.model("User", UserSchema);


This will let us track each Telegram user’s clicks in the database.


🌐 Creating the Click API Route

Now create the file app/api/clicks/route.ts:


import { connectDB } from "@/lib/db";
import { User } from "@/lib/models/User";
import { NextResponse } from "next/server";

export async function POST(req: Request) {
const { telegramId } = await req.json();
if (!telegramId) return NextResponse.json({ error: "Missing telegramId" }, { status: 400 });

await connectDB();
let user = await User.findOne({ telegramId });

if (!user) {
user = new User({ telegramId });
}

user.clicks += 1;
await user.save();

return NextResponse.json({ clicks: user.clicks });
}


This API route:

  1. • Connects to the database
  2. • Finds the user by telegramId
  3. • Increments their click count
  4. • Returns the updated total


✅ At this point…

Your app:

  1. • Has a working database connection
  2. • Can store and update user click counts via API
  3. • Is ready to be connected with the Telegram WebApp frontend



🤝 Step 3: Connecting Telegram Mini App SDK with Next.js

Now that our backend and API are ready, it’s time to make our Mini App actually talk with Telegram.


📦 Installing the Telegram SDK

First, install Telegram’s WebApp SDK:


npm install @twa-dev/sdk


It’s a lightweight library that lets your web app access Telegram Mini App features - user info, theme, buttons, and more.


🧠 Understanding How It Works

When a user opens your Mini App from Telegram, Telegram injects some special data into the page context - things like their ID, username, and chat information.

The SDK lets us read that data easily and pass it to our backend.


💻 Building the UI

Let’s now create the main app screen at app/page.tsx:


"use client";

import { useEffect, useState } from "react";
import WebApp from "@twa-dev/sdk";

export default function HomePage() {
const [telegramId, setTelegramId] = useState<string | null>(null);
const [clicks, setClicks] = useState<number | null>(null);
const [loading, setLoading] = useState(false);

useEffect(() => {
// Initialize Telegram WebApp SDK
WebApp.ready();
const user = WebApp.initDataUnsafe?.user;
if (user?.id) {
setTelegramId(user.id.toString());
}
}, []);

const handleClick = async () => {
if (!telegramId) return;
setLoading(true);

const res = await fetch("/api/clicks", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ telegramId }),
});

const data = await res.json();
setClicks(data.clicks);
setLoading(false);
};

return (
<main className="flex flex-col items-center justify-center h-screen text-center bg-gray-900 text-white">
<h1 className="text-3xl font-bold mb-6">👋 Welcome to your Telegram Mini App!</h1>

{telegramId ? (
<>
<p className="mb-2 text-gray-300">Your Telegram ID: {telegramId}</p>
<button
onClick={handleClick}
disabled={loading}
className="bg-blue-500 hover:bg-blue-600 text-white px-5 py-2 rounded-xl mt-4 disabled:opacity-60"
>
{loading ? "Counting..." : "Click me!"}
</button>
{clicks !== null && (
<p className="mt-4 text-lg text-pink-400 font-semibold">
Total clicks: {clicks}
</p>
)}
</>
) : (
<p>Waiting for Telegram user data...</p>
)}
</main>
);
}


✨ What’s Happening Here

Let’s quickly break it down:

  1. • Telegram SDK Initialization
  2. • WebApp.ready() tells Telegram your app is ready to be shown.
  3. • WebApp.initDataUnsafe.user gives us user info.
  4. • Click Counter Logic
  5. • When the button is pressed, a POST request hits /api/clicks.
  6. • The backend saves (or creates) the user and increments their clicks.
  7. • Realtime Update
  8. • The new click count is shown right away on screen.



🧩 Step 4: Testing Your Telegram Mini App Locally with ngrok


⚙️ 1️⃣ Run your local Next.js server

Make sure your Mini App runs correctly on your local machine.

In your project folder, start the dev server:


npm run dev


You should see something like:


Local: http://localhost:3000


This means your app is live locally on port 3000. Now we need to make it accessible from the internet - so Telegram can load it.


🌐 2️⃣ Expose your app with ngrok

Install ngrok


npm install -g ngrok


Then log in with your token (you’ll get it after creating an ngrok account):


ngrok authtoken YOUR_AUTHTOKEN


Now start ngrok and tunnel your local app:


ngrok http 3000


You’ll see output like this:


Forwarding https://abcd1234.ngrok.io -> http://localhost:3000


Copy the HTTPS URL (e.g. https://abcd1234.ngrok.io) - this will be the public address you’ll use for your Mini App inside Telegram.


🤖 3️⃣ Create your bot with @BotFather

Open Telegram and start a chat with @BotFather

Type:


/newbot


Then:

  1. • Give your bot a name (e.g. Click Counter Mini App)
  2. • Choose a username
  3. • Copy the API token you receive - you’ll need it later.


🧱 4️⃣ Create the Mini App (Web App)

In the same chat, type:


/newapp


BotFather will ask for a few details:

  1. • Title: Click Counter Demo
  2. • Short description: A simple Telegram Mini App built with Next.js
  3. • URL: paste your ngrok link, e.g. https://abcd1234.ngrok.io
  4. • App icon (optional): skip this for now.


BotFather will confirm that your Mini App has been created 🎉


🚀 5️⃣ Add an “Open App” button to your bot

Now, type:


/setmenubutton


Select your bot → choose Web App type → set button text (for example Open Mini App) → and again, paste your ngrok URL (https://abcd1234.ngrok.io).


📱 6️⃣ Test your Mini App

Open your bot in Telegram → press Start → tap the Open Mini App button.

Your Next.js app should open inside Telegram. If everything works, you just created your first Telegram Mini App. 🪄


🧠 7️⃣ What’s happening under the hood

  1. • When you open the app, Telegram injects user info into window.Telegram.WebApp.initDataUnsafe.
  2. • Your frontend can read it to identify users.
  3. • Your /api/clicks endpoint handles API calls and stores data in MongoDB.


⚡ 8️⃣ Pro tips

  1. • Every time you restart ngrok, your link changes - you’ll need to update it via /setmenubutton in BotFather.
  2. • To avoid that, you can deploy your app to Vercel - it’s free and works perfectly with Next.js.
  3. • Telegram only allows HTTPS URLs, so local http://localhost:3000 won’t work directly.
AdRadar
© 2025 AdRadar. All rights reserved.
Made with for Mini Apps