QR codes that live inside Telegram. Generate one in any chat, in a private conversation with the bot, or in a full editor that opens as a Telegram Mini App.
Full guides for developers, administrators and users, in three languages: English · Oʻzbekcha · Русский.
Want to help? Read CONTRIBUTING.md. Found a security problem? Follow SECURITY.md and report it privately.
| Surface | How it works |
|---|---|
| Inline | Type @kyuarbot https://example.com in any chat and pick a color |
| Private chat | Send the bot any text and get a QR code back |
| Mini App | Tap the button to open the editor, then download or share |
| Scan | Scan with the Telegram camera, from a photo, or send the bot a photo of a code |
All three render through the same engine, so a code made inline looks identical to one made in the editor.
Most generators give you black squares on white. kyuar styles every part of the symbol on its own: data modules (29 shapes), finder rings and eyes, alignment and timing patterns, background, margin and logo area. Each layer takes a solid color or a linear or radial gradient. Halftone mode turns a picture into the code itself.
Every shape is checked by a decoder in the test suite. A style that does not scan does not ship.
Every theme is contrast-checked before it ships. A palette below 4.5:1 does not scan reliably on a real camera, so it does not make it into the app.
- Node.js 24 or newer
- pnpm 11 or newer
- just
cloudflaredfor local development, since Telegram needs an HTTPS webhook
just setupThis copies .env.example to .env, symlinks that single file into every
workspace package, and installs dependencies. Then fill in .env:
just secret # generates a value for BOT_WEBHOOK_SECRETRun the app:
just dev # web app on :3000 plus the bot in long-polling modeTo test inline mode and the Mini App you need a public HTTPS URL:
just tunnel # in a second terminal
# put the tunnel URL in APP_URL, restart `just dev`, then:
just webhook-setThese steps cannot be automated and have to be done once in @BotFather:
/newbotto create the bot and copy the token intoBOT_TOKEN./setinlineto enable inline mode, with a placeholder such asPaste a link to turn it into a QR code./setinlinefeedbackset toEnabledif you later want usage statistics.- Bot Settings → Configure Mini App → enable the Main Mini App and point it at
APP_URL. Share buttons open it witht.me/<bot>?startapp=…, which works in groups and channels whereweb_appbuttons do not.
Run just to list every recipe. The ones you need most:
just fix # oxlint --fix, then oxfmt
just check # lint, format check, typecheck, react-doctor, knip
just test # unit tests
just build # production buildapps/web Next.js 16 app: Mini App UI, /api/qr, /api/bot, /api/share
apps/bot grammy handlers and the development long-polling runner
packages/qr styled SVG renderer: shapes, paints, halftone, logo area
packages/qr-encoder QR encoder with per-module kinds, vendored from paulmillr/qr
packages/ui shadcn/ui components on Base UI
packages/shared zod schemas, option codec, Telegram initData verification
packages/env envin schema, the single source of truth for configuration
brand/ logo, icons and Lottie animation
The bot runs inside the web app as a webhook route in production, and as a separate long-polling process in development. There is only one deployment.
just docker-build
just docker-upAll configuration is read from .env at runtime through env_file. The image
has no build args, so one image runs in any environment and no secret enters an
image layer.
Put a reverse proxy cache in front of /api/qr. The route already sends
Cache-Control: immutable, so repeated codes never reach Node.
- Saved qr codes per user, backed by Postgres
- paulmillr/qr by Paul Miller: the QR
encoder in
packages/qr-encoder, and the decoder the tests use. - liquid-js/qr-code-styling by
Denys Kozak and Liquid-JS: the dot, finder ring and finder eye shapes in
packages/qr/src/figures. - Hung-Kuo Chu, Chia-Sheng Chang, Ruen-Rone Lee and Niloy J. Mitra, "Halftone QR Codes" (SIGGRAPH Asia 2013): the method behind halftone mode.
Full license texts are in THIRD_PARTY_NOTICES.md.
AGPL-3.0-only. See LICENSE. Third-party code keeps its own license, see THIRD_PARTY_NOTICES.md.