Telegram actually has three separate ways to format text, plus a Unicode trick for the places none of them reach — and mixing them up is where most people get stuck. Here's the short version, then the full map.
The one path that works everywhere: in any chat, on any client (iOS, Android, Desktop, macOS, Web), select the text you typed and a formatting menu appears. That menu gives you bold, italic, underline, strikethrough, monospace, spoiler, quote, and link. On Desktop and Web you also get typed markdown and keyboard shortcuts. Bots use a completely different system. And channel titles, display names, and bios accept none of it — there you paste Unicode styled characters instead.
The universal path: select your text
This is the method that behaves the same on every Telegram app, and it's the one to remember if you only learn one thing.
Type your message first. Then highlight the words you want to style. How you reach the menu differs slightly by platform:
- iOS: long-press the text, then choose the formatting option (the B/U or Format item) from the popup.
- Android: select the text, then use the context menu or the three-dot overflow.
- Desktop / macOS: right-click the selection and choose Formatting, or use the small toolbar that appears.
The styles available through this menu are the ones Telegram supports in messages: bold, italic, underline, strikethrough, monospace (code), spoiler, blockquote (quote), and inline link. This is real formatting — it renders correctly for everyone who sees the message, and screen readers handle it properly because the underlying letters stay normal.
Desktop & Web extras: typed markdown and shortcuts
Telegram Desktop and Telegram Web add two conveniences on top of the selection menu.
Typed markdown in the compose box. As you write, you can type shorthand and Telegram converts it on send:
**bold** → bold
_italic_ → italic
__underline__ → underline
~~strikethrough~~ → strikethrough
`inline code` → inline code
```code block``` → a fixed-width code block
||spoiler|| → a hidden spoiler
Note the double asterisks for bold here, and that a single underscore is italic while a double underscore is underline. That asterisk detail matters later, because the Bot API does the opposite.
Keyboard shortcuts (Desktop and Web):
| Action | Shortcut |
|---|
| Bold | Ctrl/Cmd + B |
| Italic | Ctrl/Cmd + I |
| Underline | Ctrl/Cmd + U |
| Strikethrough | Ctrl + Shift + X |
| Monospace | Ctrl + Shift + M |
| Remove formatting | Ctrl + Shift + N |
| Insert link | Ctrl + K (or Ctrl + Shift + K) |
One honest caveat: typed-markdown auto-conversion is reliable on Desktop and Web, but inconsistent across mobile clients and app versions — some Android and iOS builds convert **bold** on send, others leave it as raw text. So on phones, the select-then-menu route is the dependable one. The keyboard shortcuts above are a Desktop/Web feature; the phone apps don't offer them.
If you're building a bot, formatting works through a different mechanism entirely. The bot sets a parse_mode on each sendMessage, choosing one of three values: MarkdownV2 (the modern, recommended mode), HTML, or Markdown (legacy, kept only for backward compatibility).
MarkdownV2 syntax (note: single asterisk for bold, single underscore for italic, double for underline):
*bold*
_italic_
__underline__
~strikethrough~
||spoiler||
`inline code`
```language
code block
link text
That single asterisk is the **opposite** of the in-app double-asterisk convention. The two systems are not interchangeable — `*bold*` is bold to a bot but not to a human typing in Desktop, and `**bold**` is bold to a human but raw text to a bot. (The underscore tokens, helpfully, *are* the same in both: single = italic, double = underline.)
**HTML mode** uses tags instead: `<b>` or `<strong>`, `<i>` or `<em>`, `<u>` or `<ins>`, `<s>`/`<del>`/`<strike>`, `<span class="tg-spoiler">` (or `<tg-spoiler>`), `<code>`, `<pre>`, `<a href="...">`, and `<blockquote>`.
Two practical warnings:
1. **MarkdownV2 demands escaping.** Every literal `_ * [ ] ( ) ~ \` > # + - = | { } . !` must be preceded by a backslash. One unescaped character causes the entire send to error out. This strict escaping is the main reason many developers prefer HTML mode — it's far more forgiving.
2. **`code` and `pre` don't nest — at all.** Bold, italic, underline, strikethrough, and spoiler can be nested with each other and combined with other entities, but `` `code` `` and `pre` blocks can neither contain another styled entity nor be contained by one. Don't try to bold text inside a `` `code` `` span, and don't wrap a code block in another style.
## Where the menu can't go: names, channels, bios
Here's the gap. The formatting menu and markdown only work inside **message bodies**. They do nothing in:
- channel and group **titles**
- user **display names** (first and last name)
- **bios**
Those fields render plain text and ignore markdown entirely. There's no menu to summon. So how do people get a bold channel name or a script-style display name?
They paste **Unicode styled characters** — distinct code points that already *look* bold, italic, monospace, or cursive (𝗯𝗼𝗹𝗱, 𝘪𝘵𝘢𝘭𝘪𝘤, 𝚖𝚘𝚗𝚘, 𝓼𝓬𝓻𝓲𝓹𝓽). Because the style is baked into the character rather than applied as a formatting layer, it survives into these plain-text fields and displays across platforms. This is exactly what a tool like BoldlyType's [Telegram text formatter](/telegram-text-formatter) produces. (Curious how the swap works under the hood? See [how bold text generators work](/blog/how-bold-text-generators-work).)
A few honest trade-offs come with Unicode text. It isn't real letters, so it carries real costs: **screen readers** often read each character by its full Unicode name — "mathematical bold capital H, mathematical bold small I" for "Hi" — or go silent on glyphs they can't name; either way the result is unintelligible to anyone listening. **In-app search** won't match it against the plain word. And rarer styles can render as empty **boxes** on devices missing the glyph. So use it for decoration, and keep anything load-bearing in plain text.
**One firm limit:** your **@username** — the actual handle people type to find you — only accepts ASCII letters, digits, and underscores. Unicode styled characters will *not* work in the @handle. They work only in the display name, channel/group title, and bio. Style the name; keep the handle plain.
## The quick mental model
If you remember nothing else: in a **message**, select your text and use the menu — it's real, accessible formatting that works on every client. On **Desktop or Web**, type markdown (`**bold**`, `_italic_`, `__underline__`, `||spoiler||`) or hit Ctrl/Cmd+B if you'd rather. If you're a **bot**, set `parse_mode` and mind that MarkdownV2 flips the asterisk count and demands escaping. And for the **names, titles, and bios** the menu can't touch, paste Unicode styled characters — just never in your @username, and never for words that have to stay searchable or screen-reader-friendly.