Slack Block Kit Markdown: mrkdwn, the Markdown Block and Lists
Block Kit text objects with type mrkdwn use Slack's own syntax: *bold*, _italic_, <url|text> links and <@U123> mentions. The markdown block takes standard markdown, with lists, headings and tables, up to 12,000 characters per payload. Syntax tables, JSON you can paste, bullet lists, links and mentions.
On this page
Slack Block Kit has two kinds of markup. A text object with "type": "mrkdwn", used in section and context blocks, reads Slack's own syntax: *bold*, _italic_, ~strike~, <https://example.com|link text> and <@U012AB3CD> for a mention. The markdown block, "type": "markdown", reads standard markdown instead: **bold**, [text](url), - lists, # headings and even tables. One markdown block takes up to 12,000 characters; in our test two blocks together passed at 13,200 characters and failed at 13,250. If your text comes from an LLM or a README, use the markdown block. If you build the message yourself and need mentions, dates or buttons, use mrkdwn.
This page covers apps, bots and webhooks. People typing in Slack's composer can now use [text](link) in markup mode, but API and bot mrkdwn still needs <url|text>.
We posted every limit and mention form on this page with a bot token in a test workspace on 4 October; the results are in the tables below. When you cannot use the markdown block, the free Markdown to Slack converter turns standard markdown into mrkdwn.
mrkdwn vs the markdown block
| Style | mrkdwn text object | markdown block |
|---|---|---|
| Bold | *bold* | **bold** or __bold__ |
| Italic | _italic_ | *italic* or _italic_ |
| Strikethrough | ~strike~ | ~~strike~~ |
| Link | Angle brackets, shown below | [text](https://x.com) |
| Bullet list | Not supported, type • | - item |
| Numbered list | Not supported | 1. item |
| Heading | Not supported | # Heading (all levels render the same size) |
| Table | Not supported | Pipe table |
| Superscript | Not supported, paste Unicode ² | Not supported: ^2^ and <sup>2</sup> stayed literal in our superscript test |
| Code block | Three backticks | Three backticks, with a language for highlighting |
| Limit | 3,000 characters per section text (3,001 failed in our test) | 12,000 per block; about 13,200 for all markdown blocks in one message (measured) |
The asterisk is the trap. *this* is bold in mrkdwn and italic in the markdown block. Text written for one reads wrong in the other.
The markdown block
Slack's reference describes the markdown block as the way to post markdown from an LLM without translating it yourself. Slack does the conversion, and one markdown block may turn into several blocks in the posted message.
{
"blocks": [
{
"type": "markdown",
"text": "## Deploy report\n**Status:** passed\n\n- api: 42 s\n- web: 1 min 10 s\n\n| Service | Version |\n| --- | --- |\n| api | 3.4.1 |\n| web | 3.4.0 |\n\n[Open the run](https://github.com/acme/app/actions)"
}
]
}
Things to know:
- •
block_idis ignored and not kept. - • Images written as
turn into a link with the alt text, not an image. Use an image block for pictures. - • Task lists (
- [ ]) and horizontal rules (---) render. - • Escape a markdown character with a backslash, such as
\*not bold\*.
mrkdwn in section blocks
Most Block Kit layouts still use section blocks with a mrkdwn text object:
{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Build failed* on `main`\n<https://github.com/acme/app/actions|Open the run> and ping <@U012AB3CD>"
}
}
]
}
Some fields only accept plain_text, such as header blocks and button labels. In those, asterisks stay asterisks. Slack's developer docs mark which fields allow which type. The top-level text of chat.postMessage uses mrkdwn by default; set "mrkdwn": false to turn formatting off.
Links, mentions and dates in mrkdwn
Link with text <https://slack.green|Slack Green>
Email link <mailto:hi@example.com|Email us>
Mention a user <@U012AB3CD>
Link a channel <#C123ABC456>
Mention a user group <!subteam^SAZ94GDB8>
@here, @channel <!here> <!channel> <!everyone>
Date in reader's zone <!date^1727481600^{date_short} at {time}|Sep 28>
Use the user ID in mentions. A name works in some places and not others. We sent <@sieun> (a username) and <@U0B7L4YK420> (the same person's ID) with a bot token through chat.postMessage:
Where <@sieun> was sent | What Slack posted |
|---|---|
Top-level text | A real mention; Slack stored it as <@U0B7L4YK420> |
| mrkdwn text in a section block | A real mention; stored as <@U0B7L4YK420> |
| markdown block | The literal text <@sieun>; only the ID form became a mention |
In an earlier test a user token resolved the name the same way. This lookup is not in Slack's docs, so build mentions from IDs anyway: a renamed user breaks a name, never an ID. Slack's docs add that a user mentioned by an app is notified only if they are already in the channel. Date strings are easier to build in our Slack timestamp generator, and the full link rules are in how to add a hyperlink in Slack.
Slack treats &, < and > as control characters. Replace them with &, < and > in any text you pass through, and only those three.
For webhook mentions, finding member IDs and a lookup-by-email script, see how to mention a user in a Slack webhook.
Never appear "away" on Slack again
Cloud-based. No downloads. Works 24/7 even when your laptop is off.
Bullet lists in Block Kit
Slack's formatting docs say plainly that there is no list syntax in app-published mrkdwn. You have three options:
"• first\n• second" in a mrkdwn text. Looks like a list, but long items do not indent when they wrap.- first works there.{
"type": "rich_text",
"elements": [
{
"type": "rich_text_list",
"style": "bullet",
"elements": [
{ "type": "rich_text_section", "elements": [{ "type": "text", "text": "first item" }] },
{ "type": "rich_text_section", "elements": [{ "type": "text", "text": "second item" }] }
]
}
]
}
Use "style": "ordered" for a numbered list. Nested levels, quotes, code blocks and the error each wrong payload returns are in our rich text block tests. People typing in Slack get lists from the composer; see how to make bullet points in Slack.
Test before you send
Paste any payload into Slack's Block Kit Builder to see it rendered; the steps are in how to use Slack Block Kit Builder. For the mrkdwn part alone, the Slack to Markdown side of our Markdown to Slack converter previews the text. If formatting shows up as raw asterisks, Slack markdown not working lists the causes. Length limits for each field are in Slack message character limit, and tables are in how to make a table in Slack.
To post a whole markdown file, such as a README, create a canvas from it instead; see Slack markdown file preview.
Block Kit text limits we measured
Each row is one chat.postMessage call with a bot token:
| What we sent | Result |
|---|---|
| One markdown block, 12,000 characters | ok |
| One markdown block, 12,001 characters | msg_too_long |
| Two markdown blocks, 6,001 + 6,000 | ok |
| Two markdown blocks, 6,600 + 6,600 | ok |
| Two markdown blocks, 6,625 + 6,625 | msg_blocks_too_long |
| Three markdown blocks, 6,000 each | msg_blocks_too_long |
| Three markdown blocks, 4,300 each | ok |
| mrkdwn section text, 3,001 characters | invalid_blocks: "must be less than 3001 characters" |
So the 12,000 figure is per markdown block, and the whole message has a second cap a little above 13,000 characters. Split longer reports across messages.
FAQ
Does Slack Block Kit support markdown? Yes, two ways: mrkdwn text objects with Slack's own syntax, and the markdown block with standard markdown.
What is the difference between mrkdwn and markdown in Slack?
mrkdwn is Slack's syntax: *bold*, <url|text>. The markdown block reads standard markdown: **bold**, [text](url).
How do I make a bullet list in Block Kit?
Use - item in a markdown block, or a rich_text block with a rich_text_list. mrkdwn has no list syntax.
How do I mention a user in Block Kit?
<@U012AB3CD> with the user ID, inside a mrkdwn text.
How long can a markdown block be? 12,000 characters for all markdown blocks in one payload together.
Stop Jiggling Your Mouse.
Join hundreds of remote workers who never worry about their Slack status. Set it up once, stay green forever.
Related Articles
Slack Bot Scopes: The Scope Each API Method Needs, Measured on 44 Methods
We gave a Slack bot one scope, called 44 common Web API methods, and read the needed field of every missing_scope error. Then we added the scopes and called them again. The table shows which scope clears each method.
Slack File Size Limit: 1 GB Is Exactly 1,073,741,824 Bytes, Tested
We uploaded files of 50 MB, exactly 1 GiB and 1 GiB plus one byte to Slack through its upload API. The limit is 1,073,741,824 bytes, and a failed upload can still leave a broken file behind.
Slack Channel Name Rules: 45 Names Tested in the App and the API
Slack channel names take lowercase letters, numbers, hyphens, underscores and letters from other scripts, up to 80 characters. We sent 45 names to the Slack app and the API and recorded every error.