Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .changeset/slack-native-markdown.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
"@chat-adapter/slack": minor
---

use Slack's native `markdown_text` field for outgoing markdown messages

Slack now natively renders markdown via the `markdown_text` parameter on
`chat.postMessage`, `chat.postEphemeral`, `chat.update`, and
`chat.scheduleMessage`. The adapter passes markdown through directly instead
of converting to mrkdwn, so tables, headings, fenced code blocks, blockquotes,
and other rich formatting now render natively in Slack.

- Tables are rendered by Slack natively (no more ASCII-table fallback or
Block Kit `table` block fabrication).
- Plain `string` and `{ raw }` messages still go to the `text` field so
literal `*` / `_` characters are preserved.
- `markdown_text` has a 12,000 character limit (vs. ~40,000 for `text`).
- The deprecated `SlackMarkdownConverter` alias has been removed; use
`SlackFormatConverter` instead.
- `renderFormatted(ast)` now returns standard markdown instead of mrkdwn.
- Incoming `message` events are unchanged — they still arrive as mrkdwn
and are parsed as before.
14 changes: 9 additions & 5 deletions apps/docs/content/docs/api/markdown.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ const value = getNodeValue(node); // string | undefined

### tableToAscii

Render an mdast `Table` node as a padded ASCII table string. Used by adapters that lack native table support (Slack, Google Chat, Discord, Telegram).
Render an mdast `Table` node as a padded ASCII table string. Used by adapters that lack native table support (Google Chat, Discord, Telegram).

```typescript
import { parseMarkdown, tableToAscii, isTableNode } from "chat";
Expand Down Expand Up @@ -280,17 +280,21 @@ The SDK uses mdast as the canonical format and each adapter converts it to the p

| Feature | Slack | Teams | Google Chat |
|---------|-------|-------|-------------|
| Bold | `*text*` | `**text**` | `*text*` |
| Bold | `**text**` | `**text**` | `*text*` |
| Italic | `_text_` | `_text_` | `_text_` |
| Strikethrough | `~text~` | `~~text~~` | `~text~` |
| Strikethrough | `~~text~~` | `~~text~~` | `~text~` |
| Code | `` `code` `` | `` `code` `` | `` `code` `` |
| Code blocks | ```` ``` ```` | ```` ``` ```` | ```` ``` ```` |
| Links | `<url\|text>` | `[text](url)` | `[text](url)` |
| Links | `[text](url)` | `[text](url)` | `[text](url)` |
| Lists | Supported | Supported | Supported |
| Blockquotes | `>` | `>` | Simulated with `>` prefix |
| Tables | ASCII fallback | Native GFM | ASCII fallback |
| Tables | Native (markdown_text) | Native GFM | ASCII fallback |
| Mentions | `<@USER>` | `<at>name</at>` | `<users/{id}>` |

<Callout type="info">
Slack accepts standard markdown via the `markdown_text` field on `chat.postMessage` and friends, so the SDK passes markdown through directly. Incoming Slack messages still arrive as legacy mrkdwn (`*bold*`, `<url|text>`) and are parsed transparently. If you need to send mrkdwn yourself, use `{ raw: "..." }`.
</Callout>

<Callout type="info">
You don't need to worry about these differences when using the SDK — the AST builders and `parseMarkdown` handle conversion automatically. This table is useful if you're working with `raw` platform payloads or debugging formatting issues.
</Callout>
2 changes: 1 addition & 1 deletion apps/docs/content/docs/contributing/building.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,7 @@ export class MatrixFormatConverter extends BaseFormatConverter {
}
```

For platforms with non-standard formatting (e.g., Slack's `mrkdwn`), implement custom parsing in `toAst()` and rendering in `fromAst()`. See the [Discord adapter](https://github.com/vercel/chat/blob/main/packages/adapter-discord/src/markdown.ts) for an example of handling platform-specific mention syntax.
For platforms with non-standard formatting, implement custom parsing in `toAst()` and rendering in `fromAst()`. See the [Discord adapter](https://github.com/vercel/chat/blob/main/packages/adapter-discord/src/markdown.ts) for an example of handling platform-specific mention syntax.

## Optional methods

Expand Down
4 changes: 2 additions & 2 deletions apps/docs/content/docs/posting-messages.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ This sends the string directly without any formatting conversion.

## Markdown

Pass a `{ markdown }` object to have the SDK convert standard markdown to each platform's native format — mrkdwn for Slack, HTML for Teams, and so on.
Pass a `{ markdown }` object to have the SDK render standard markdown on each platform — passed through to Slack's native `markdown_text` field, converted to HTML for Teams, and so on.

```typescript title="lib/bot.ts" lineNumbers
await thread.post({
markdown: "**Bold**, _italic_, and `code`",
});
```

Under the hood, the SDK parses the markdown into an mdast AST, then each adapter converts it to the platform's format.
Under the hood, the SDK parses the markdown into an mdast AST, then each adapter handles it natively or converts it to the platform's format.

## AST builders

Expand Down
22 changes: 15 additions & 7 deletions packages/adapter-slack/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1326,7 +1326,7 @@ describe("renderFormatted", () => {
logger: mockLogger,
});

it("renders AST to Slack mrkdwn format", () => {
it("renders AST to standard markdown (Slack now accepts markdown natively)", () => {
const ast = {
type: "root" as const,
children: [
Expand All @@ -1343,7 +1343,7 @@ describe("renderFormatted", () => {
};

const result = adapter.renderFormatted(ast);
expect(result).toBe("*bold*");
expect(result.trim()).toBe("**bold**");
});
});

Expand Down Expand Up @@ -5303,16 +5303,24 @@ describe("editMessage via response_url", () => {
.spyOn(global, "fetch")
.mockResolvedValue(new Response("ok", { status: 200 }));

await adapter.editMessage(
"slack:C123:1234567890.000000",
ephemeralId,
"Updated text"
);
await adapter.editMessage("slack:C123:1234567890.000000", ephemeralId, {
markdown:
"**Updated** [text](https://example.com)\n\n| A | B |\n|---|---|\n| 1 | 2 |",
});

expect(fetchSpy).toHaveBeenCalledWith(
"https://hooks.slack.com/respond",
expect.objectContaining({ method: "POST" })
);
const callBody = JSON.parse(fetchSpy.mock.calls[0][1]?.body as string);
expect(callBody).toEqual(
expect.objectContaining({
replace_original: true,
text: expect.stringContaining("*Updated* <https://example.com|text>"),
})
);
expect(callBody.text).toContain("```");
expect(callBody).not.toHaveProperty("markdown_text");
fetchSpy.mockRestore();
});
});
Expand Down
Loading