md2slack

package module
v0.0.0-...-7ae9033 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 6, 2026 License: MIT Imports: 11 Imported by: 0

README

md2slack

Go Reference CI Go Report Card

Convert standard Markdown into Slack Block Kit blocks.

  • AST-based parsing — uses goldmark with GFM extensions for correct handling of nested/complex markdown
  • Native Slack types — returns []slack.Block from slack-go/slack, ready for direct API use
  • Rich output — headers, dividers, images, rich text (bold, italic, strikethrough, code), lists with nested indent, action buttons, blockquotes, code blocks, and tables
  • GFM support — tables, strikethrough, task checkboxes, autolinks
  • Message chunkingChunkBlocks splits output for Slack's 50-block-per-message limit

Install

go get github.com/navidemad/md2slack

Requires Go 1.25+.

Quick start

import (
	"github.com/navidemad/md2slack"
	"github.com/slack-go/slack"
)

blocks, err := md2slack.Convert("# Welcome\n\nHello **world**.\n\n---\n\n![banner](https://example.com/banner.png)")

Output blocks (as JSON):

[
  {
    "type": "header",
    "text": { "type": "plain_text", "text": "Welcome" }
  },
  {
    "type": "rich_text",
    "elements": [
      {
        "type": "rich_text_section",
        "elements": [
          { "type": "text", "text": "Hello " },
          { "type": "text", "text": "world", "style": { "bold": true } },
          { "type": "text", "text": "." }
        ]
      }
    ]
  },
  { "type": "divider" },
  {
    "type": "image",
    "image_url": "https://example.com/banner.png",
    "alt_text": "banner"
  }
]
Block type mapping
Markdown construct Block Kit block type
# Heading through ###### header (plain_text, max 150 chars; falls back to bold mrkdwn section for links or overflow)
---, ***, ___ divider
![alt](url) (standalone) image
[text](url) (standalone) actions (button element)
> quote rich_text (rich_text_quote)
Fenced code blocks (```) rich_text (rich_text_preformatted)
1. item / - item rich_text (rich_text_list with ordered/bullet style, nested indent)
GFM tables table (native TableBlock with rich text cells, per-column alignment and wrapping)
Inline text with formatting rich_text (rich_text_section with styled elements)
Inline formatting

Bold, italic, strikethrough, and inline code are represented as RichTextSectionTextElement entries with style flags rather than mrkdwn strings:

blocks, _ := md2slack.Convert("**bold** and _italic_ and ~~strike~~ and `code`")

Links become RichTextSectionLinkElement with the URL and display text.

Lists

Lists are emitted as rich_text blocks with rich_text_list elements. Nested sub-lists use Slack's indent field:

blocks, _ := md2slack.Convert("1. First\n   - Sub-item A\n   - Sub-item B\n2. Second")
[
  {
    "type": "rich_text",
    "elements": [
      {
        "type": "rich_text_list",
        "style": "ordered",
        "indent": 0,
        "elements": [
          {
            "type": "rich_text_section",
            "elements": [{ "type": "text", "text": "First" }]
          }
        ]
      },
      {
        "type": "rich_text_list",
        "style": "bullet",
        "indent": 1,
        "elements": [
          {
            "type": "rich_text_section",
            "elements": [{ "type": "text", "text": "Sub-item A" }]
          },
          {
            "type": "rich_text_section",
            "elements": [{ "type": "text", "text": "Sub-item B" }]
          }
        ]
      },
      {
        "type": "rich_text_list",
        "style": "ordered",
        "indent": 0,
        "elements": [
          {
            "type": "rich_text_section",
            "elements": [{ "type": "text", "text": "Second" }]
          }
        ]
      }
    ]
  }
]

A paragraph containing only a markdown link becomes an actions block with a clickable button:

blocks, _ := md2slack.Convert("[Click here](https://example.com)")
[
  {
    "type": "actions",
    "elements": [
      {
        "type": "button",
        "text": { "type": "plain_text", "text": "Click here" },
        "url": "https://example.com"
      }
    ]
  }
]
Message chunking

Slack limits messages to 50 blocks. Use ChunkBlocks to split. It also enforces at most one TableBlock per chunk, since Slack rejects messages containing multiple tables.

blocks, _ := md2slack.Convert(longMarkdown)
chunks := md2slack.ChunkBlocks(blocks, 50)
for _, chunk := range chunks {
    // post each chunk as a separate message
}

API

Convert(markdown string) ([]slack.Block, error)

Parses a Markdown string and returns Slack Block Kit blocks. Returns nil, nil for empty input.

ChunkBlocks(blocks []slack.Block, maxPerMessage int) [][]slack.Block

Splits a block slice into chunks of at most maxPerMessage. Defaults to 50 if maxPerMessage <= 0. Each chunk contains at most one TableBlock — when a table is encountered, the current chunk is finalized and a new chunk begins after the table.

CLI example

The cmd/example directory contains a CLI tool that reads markdown from stdin or a file and prints the Block Kit JSON:

echo "## Hello\n\n**bold** and _italic_" | go run ./cmd/example/

Dependencies

Package Purpose
github.com/yuin/goldmark GFM-compliant Markdown parser (AST-based)
github.com/slack-go/slack Canonical Slack Block Kit types

Demo

See DEMO.md for a standalone script that posts converted Markdown to a Slack channel.

Contributing

Fork the repository and open a pull request. Contributions are welcome!

License

MIT

Documentation

Overview

Package md2slack converts standard Markdown into Slack Block Kit blocks using proper AST-based parsing via goldmark and canonical Slack types from slack-go/slack.

It provides two main functions:

  • Convert parses Markdown (including GFM extensions) and returns []slack.Block suitable for use in the "blocks" field of Slack API payloads. Headings become HeaderBlocks, horizontal rules become DividerBlocks, images become ImageBlocks, fenced code becomes RichTextPreformatted, lists become RichTextList, blockquotes become RichTextQuote, tables become TableBlocks with rich text cells, and inline text with formatting becomes RichTextSection elements.

  • ChunkBlocks splits a block slice into chunks of at most N blocks, useful for respecting Slack's 50-block-per-message limit. It also ensures each chunk contains at most one TableBlock, since Slack rejects messages with multiple tables.

Supported Markdown features

  • Headings (# through ######) → HeaderBlock (plain_text, max 150 chars)
  • Bold (**text**) → RichTextSectionTextStyle{Bold: true}
  • Italic (*text* / _text_) → RichTextSectionTextStyle{Italic: true}
  • Bold+Italic (***text***) → Both Bold and Italic
  • Strikethrough (~~text~~) → RichTextSectionTextStyle{Strike: true}
  • Inline code (`code`) → RichTextSectionTextStyle{Code: true}
  • Links (text(url)) → RichTextSectionLinkElement
  • Images (![alt](url)) → ImageBlock (standalone) or link (inline)
  • Fenced code blocks (```) → RichTextPreformatted
  • Blockquotes (> text) → RichTextQuote
  • Ordered lists (1. item) → RichTextList (ordered)
  • Unordered lists (- item) → RichTextList (bullet)
  • Nested lists → RichTextList with indent levels
  • GFM tables → TableBlock with rich text cells
  • Horizontal rules (---) → DividerBlock
  • Standalone links → ActionBlock with button
  • Task checkboxes (- [x] item) → checkbox emoji
  • Reference-style links (text[ref]) → resolved via goldmark

Dependencies

This package uses goldmark for Markdown parsing and slack-go/slack for canonical Slack Block Kit types.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ChunkBlocks

func ChunkBlocks(blocks []slack.Block, maxPerMessage int) [][]slack.Block

ChunkBlocks splits a slice of blocks into chunks of at most maxPerMessage, while also ensuring each chunk contains at most one slack.TableBlock. Slack rejects messages with more than one table (error: only_one_table_allowed), so this function forces a new chunk before every additional TableBlock. If maxPerMessage is <= 0, it defaults to 50.

func Convert

func Convert(markdown string) ([]slack.Block, error)

Convert parses a Markdown string and returns Slack Block Kit blocks.

It uses goldmark with GFM extensions to parse the markdown into an AST, then walks the tree to build structured Slack blocks. The resulting blocks can be used directly with the slack-go library to post messages.

Supported markdown features:

  • Headings (# through ######) → HeaderBlock (plain_text, max 150 chars)
  • Bold (**text** / __text__) → RichTextSectionTextStyle{Bold: true}
  • Italic (*text* / _text_) → RichTextSectionTextStyle{Italic: true}
  • Strikethrough (~~text~~) → RichTextSectionTextStyle{Strike: true}
  • Inline code (`code`) → RichTextSectionTextStyle{Code: true}
  • Links (text(url)) → RichTextSectionLinkElement
  • Images (![alt](url)) → ImageBlock (standalone) or link element (inline)
  • Fenced code blocks (```) → RichTextBlock with RichTextPreformatted
  • Blockquotes (> text) → RichTextBlock with RichTextQuote
  • Ordered lists (1. item) → RichTextBlock with RichTextList (ordered)
  • Unordered lists (- item) → RichTextBlock with RichTextList (bullet)
  • Nested lists → RichTextList with indent levels
  • Tables (GFM) → TableBlock with rich text cells
  • Horizontal rules (---) → DividerBlock
  • Standalone links (text(url) alone) → ActionBlock with button
  • Task checkboxes (- [x] item) → checkbox emoji text

An empty string returns nil and no error.

Types

This section is empty.

Directories

Path Synopsis
cmd
demo command
example command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL