@gramio/format
v0.12.1
Published
Library for formatting text for Telegram Bot API
Maintainers
Readme
@gramio/format
Library for formatting text for Telegram Bot API. Used under the hood by GramIO framework but it is framework-agnostic.
Installation
npm install @gramio/format
# or
bun add @gramio/formatOverview
@gramio/format builds MessageEntity objects alongside the text so you never have to track character offsets manually. All formatters compose freely — nest them as deeply as you like.
import { format, bold, italic, link, spoiler } from "@gramio/format";
const msg = format`${bold`Hi!`}
Can ${italic("you")} help ${spoiler`me`}?
Can you give me a ${link("star", "https://github.com/gramiojs/gramio")}?`;
// msg.text → the plain string
// msg.entities → ready-to-send MessageEntity[]Template literals
format
Strips leading indentation from every line (safe to indent inside your source).
import { format, bold, italic } from "@gramio/format";
format`Hello ${bold("world")}!`
// text: "Hello world!"
// entities: [{ type: "bold", offset: 6, length: 5 }]formatSaveIndents
Same as format but preserves all whitespace exactly as written.
import { formatSaveIndents, code } from "@gramio/format";
formatSaveIndents`Run:\n ${code("bun install")}`Formatters
Every formatter accepts either a plain string, a FormattableString, or a tagged template literal.
bold("text")
bold`text`
bold(italic("text")) // nested: bold wraps italic| Formatter | Telegram entity | Notes |
|---|---|---|
| bold | bold | Cannot be combined with code / pre |
| italic | italic | Cannot be combined with code / pre |
| underline | underline | Cannot be combined with code / pre |
| strikethrough | strikethrough | Cannot be combined with code / pre |
| spoiler | spoiler | Cannot be combined with code / pre |
| blockquote | blockquote | Cannot be nested |
| expandableBlockquote | expandable_blockquote | Cannot be nested |
| code | code | Inline monospace; cannot be combined with others |
| pre(text, lang?) | pre | Code block; optional language for syntax highlight |
| link(text, url) | text_link | Cannot be combined with code / pre |
| mention(text, user) | text_mention | |
| customEmoji(emoji, id) | custom_emoji | Requires purchased username on Fragment |
Examples
import {
bold, italic, underline, strikethrough, spoiler,
blockquote, expandableBlockquote,
code, pre, link, mention, customEmoji,
format,
} from "@gramio/format";
bold`Important`
italic`Subtle`
underline`Marked`
strikethrough`Removed`
spoiler`Secret`
blockquote`A quote from someone wise.`
expandableBlockquote`A very long quote that can be collapsed.`
code`npm install`
pre("console.log('hi')", "js")
link("GramIO", "https://gramio.dev")
mention("John", { id: 1, is_bot: false, first_name: "John" })
customEmoji("⚔️", "5222106016283378623")
// Compose freely
format`${bold("Price")}: ${italic("$42")}\nSee ${link("docs", "https://gramio.dev")}`join — formatting arrays
Array .join() discards entities. Use join instead:
import { join, bold, format } from "@gramio/format";
const items = ["apple", "banana", "cherry"];
format`Shopping list:\n${join(items, (item) => bold(item), "\n")}`;
// text: "Shopping list:\napple\nbanana\ncherry"
// entities: bold on each item at the correct offsetsThe third argument is the separator (default ", "). Return null, undefined, or false from the iterator to skip an item.
markdownToFormattable — Markdown → FormattableString
Converts a Markdown string to a FormattableString with proper entities. Useful when your content comes from a Markdown source (e.g., API docs, user input).
Requires marked as a peer dependency:
npm install markedimport { markdownToFormattable } from "@gramio/format/markdown";
const result = markdownToFormattable(
"# Title\n\n**bold** and *italic*\n\n> Blockquote\n\n[link](https://gramio.dev)"
);
// result.text → "Title\n\nbold and italic\n\nBlockquote\n\nlink"
// result.entities → bold, italic, blockquote, text_link at correct offsetsSupported Markdown elements
| Markdown | Result |
|---|---|
| **text** / __text__ | bold |
| *text* / _text_ | italic |
| ~~text~~ | strikethrough |
| `code` | inline code |
| ```lang\n...\n``` | pre block (with optional language) |
| > text | blockquote |
| [text](url) | link |
|  | link (image → text link) |
| # Heading … ###### Heading | bold |
| - item / 1. item | list with bullet/number prefix |
htmlToFormattable — HTML → FormattableString
Converts an HTML string to a FormattableString. Designed for HTML produced by rich-text editors such as TipTap, ProseMirror, or Quill.
Requires node-html-parser as a peer dependency:
npm install node-html-parserimport { htmlToFormattable } from "@gramio/format/html";
const result = htmlToFormattable(
"<p><strong>Hello</strong> <em>world</em></p>"
);
// result.text → "Hello world"
// result.entities → [bold offset=0 len=5, italic offset=6 len=5]Supported HTML elements
| Element | Result | Notes |
|---|---|---|
| <strong>, <b> | bold | |
| <em>, <i> | italic | |
| <u> | underline | |
| <s>, <del>, <strike> | strikethrough | |
| <code> | inline code | When not inside <pre> |
| <pre><code class="language-js"> | pre block | Language extracted from language-* class |
| <blockquote> | blockquote | Children processed recursively |
| <a href="..."> | link | |
| <h1> … <h6> | bold | |
| <p>, <div> | transparent wrapper | Children joined with "" |
| <br> | \n | |
| <ul> | unordered list | Items prefixed with - |
| <ol start="N"> | ordered list | Items numbered from start |
Block-level elements at the root are separated by \n\n. List items are separated by \n. Nested lists are supported and indented with \n before the sub-list.
TipTap example
import { htmlToFormattable } from "@gramio/format/html";
const html = `
<h1>Release notes</h1>
<p>Version <strong>2.0</strong> is out with <em>exciting</em> changes:</p>
<ul>
<li><p>New <code>htmlToFormattable</code> function</p></li>
<li><p>Performance improvements</p></li>
</ul>
<p>Read the <a href="https://gramio.dev">full docs</a>.</p>
`;
const result = htmlToFormattable(html);
// Sends correctly formatted message to Telegram with all entities intactUsing with Telegram Bot API
The FormattableString object has .text and .entities properties that map directly to Telegram's sendMessage parameters:
import { format, bold, link } from "@gramio/format";
const msg = format`Check out ${bold(link("GramIO", "https://gramio.dev"))}!`;
await bot.api.sendMessage({
chat_id: chatId,
text: msg.text,
entities: msg.entities,
});With GramIO you can pass the FormattableString directly:
bot.on("message", (ctx) => {
ctx.send(format`Hello ${bold(ctx.from.first_name)}!`);
});Rich messages and native tables
For Telegram Rich Messages, use the structured table() helper when you want a native
InputRichBlockTable instead of Markdown text:
import { blocks, table } from "@gramio/format/rich";
const report = blocks([
blocks.heading(1, "Release report"),
table(
[["Package", "Version"], ["gramio", "0.14.0"]],
{ bordered: true, striped: true, compact: true, align: ["left", "right"] },
),
]);
ctx.send(report);table() can also be sent directly. The first row is a header by default, and cell alignment
defaults to left/top. Use blocks.cell("Total", { colSpan: 2 }) for spans. The previous
Markdown/GFM serializer is available as markdownTable() for explicit Markdown composition with
rich([...]).
This is a breaking change from @gramio/format 0.11: table() used to produce Markdown/GFM.
Rename existing Markdown calls to markdownTable() when upgrading to 0.12.
For a complete Markdown/HTML string produced by your own trusted serializer, use rawRich();
it preserves the syntax that the safe rich\…`tag escapes. Never pass untrusted text torawRich()`.
When using rowSpan, omit the covered cells in following rows; column alignment follows the
logical table grid.
