Convert Markdown to Slack Block Kit blocks using mistletoe
Project description
Slack Blocks Markdown
Convert Markdown to Slack Block Kit blocks using Python. This library provides a clean, efficient way to transform your Markdown content into Slack's interactive Block Kit format.
Features
- 🚀 Complete Markdown Support: Headers, paragraphs, lists, code blocks, quotes, tables, links, and inline formatting
- 📱 Slack Block Kit Compatible: All blocks follow official Slack specifications with proper constraints
- 🎯 Custom Table Support: Full implementation of Slack's table blocks with validation
- 🔧 Flexible APIs: Dictionary-based for simplicity, object-based for editing, or direct renderer for full control
- ✏️ Editable Blocks: Modify generated blocks before sending to Slack
- ⚡ High Performance: Efficient processing of large documents
- 🧪 Well Tested: Comprehensive test suite with high coverage
- 📝 Type Safe: Full type hints for better development experience
Installation
pip install slack-blocks-markdown
Quick Start
Simple Usage (Dictionary API)
from slack_blocks_markdown import markdown_to_blocks
# Convert markdown to Slack blocks (returns list of dictionaries)
markdown = """# Project Update
This is a **bold** announcement about our new features:
- ✅ User authentication
- ✅ Real-time updates
- 🚧 Mobile app (coming soon)
> **Note**: This is still in beta, so please report any issues!
"""
blocks = markdown_to_blocks(markdown)
print(f"Generated {len(blocks)} blocks")
# Use blocks directly with Slack SDK
from slack_sdk import WebClient
client = WebClient(token="your-token")
client.chat_postMessage(
channel="#general",
blocks=blocks
)
Object-Based API (Editable Blocks)
from slack_blocks_markdown import markdown_to_block_objects
# Convert markdown to Block objects that can be modified
markdown = "# Hello World\n\nThis is a paragraph."
blocks = markdown_to_block_objects(markdown)
# Edit blocks before sending to Slack
blocks[0].text.text = "Custom Header"
blocks[1].text.text = "Modified paragraph with *custom* content."
# Convert to dictionaries when ready to send
from slack_sdk import WebClient
client = WebClient(token="your-token")
client.chat_postMessage(
channel="#general",
blocks=[block.to_dict() for block in blocks]
)
Advanced Usage (Direct Renderer)
For maximum control over the rendering process, use the renderer directly:
from mistletoe import Document
from slack_blocks_markdown import SlackBlocksRenderer
# Direct renderer usage with options
with SlackBlocksRenderer(expand_sections=True) as renderer:
document = Document(markdown)
blocks = renderer.render(document) # Returns Block objects
# Convert to dictionaries for JSON serialization
blocks_json = [block.to_dict() for block in blocks]
Supported Markdown Elements
| Markdown Element | Slack Block Type | Notes |
|---|---|---|
# Headers |
HeaderBlock | Truncated to 150 chars |
| Paragraphs | SectionBlock | With mrkdwn formatting |
**Bold** → *Bold* |
Inline formatting | Slack markdown style |
_Italic_ → _Italic_ |
Inline formatting | Slack markdown style |
`Code` → `Code` |
Inline formatting | Preserved |
~~Strike~~ → ~Strike~ |
Inline formatting | Slack markdown style |
[Link](url) → <url|Link> |
Inline formatting | Slack link format |
| Code blocks | SectionBlock | With triple backticks |
| > Blockquotes | SectionBlock | With > prefix |
| Lists | SectionBlock | Bullet (•) or numbered |
| Tables | TableBlock | Custom implementation |
--- |
DividerBlock | Horizontal rules |
Examples
Basic Formatting
markdown = "This is **bold** and _italic_ with `code` and [links](https://example.com)"
blocks = markdown_to_blocks(markdown)
# Result: [{"type": "section", "text": {"type": "mrkdwn", "text": "This is *bold* and _italic_ with `code` and <https://example.com|links>"}}]
Lists and Code
markdown = """
## Features
- Easy to use
- Fast processing
- Great documentation
```python
def hello():
return "world"
""" blocks = markdown_to_blocks(markdown)
Generates HeaderBlock, SectionBlock (list), and SectionBlock (code)
### Tables
```python
markdown = """
| Feature | Status |
|---------|--------|
| Auth | ✅ Done |
| API | 🚧 Progress |
"""
blocks = markdown_to_blocks(markdown)
# Generates custom TableBlock with proper cell structure
Editing Blocks (Object-Based API)
from slack_blocks_markdown import markdown_to_block_objects
# Generate blocks from markdown
markdown = """# Project Status
Current progress:
- Feature A: Complete
- Feature B: In progress
"""
blocks = markdown_to_block_objects(markdown)
# Modify the header dynamically
project_name = "My Awesome Project"
blocks[0].text.text = f"{project_name} - Status Report"
# Add a timestamp to the paragraph
from datetime import datetime
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M")
blocks[1].text.text = f"Report generated at {timestamp}\n\n" + blocks[1].text.text
# Convert to dictionaries and send
from slack_sdk import WebClient
client = WebClient(token="your-token")
client.chat_postMessage(
channel="#general",
blocks=[block.to_dict() for block in blocks]
)
Combining Manual and Generated Blocks
from slack_blocks_markdown import markdown_to_block_objects
from slack_sdk.models.blocks import DividerBlock, SectionBlock, MarkdownTextObject
# Generate blocks from markdown
markdown = "## Summary\n\nProject is on track."
generated_blocks = markdown_to_block_objects(markdown)
# Create custom blocks manually
custom_header = SectionBlock(
text=MarkdownTextObject(text="*Custom Section*")
)
# Combine blocks in any order
all_blocks = [
custom_header,
DividerBlock(),
*generated_blocks, # Insert all generated blocks
]
# Send to Slack
client.chat_postMessage(
channel="#general",
blocks=[block.to_dict() for block in all_blocks]
)
API Reference
markdown_to_blocks(markdown_text: str, expand_sections: bool | None = None) -> list[dict[str, Any]]
Convenience function to convert markdown to Slack block dictionaries.
Parameters:
markdown_text: Markdown formatted stringexpand_sections: (Optional) Control section block expansionTrue: Force all section blocks to be fully expandedFalse: Allow Slack to show "Show more" button for long contentNone(default): Use Slack's default behavior
Returns:
- List of Slack block dictionaries ready for API use
Example:
blocks = markdown_to_blocks("# Hello\n\nWorld", expand_sections=True)
client.chat_postMessage(channel="#general", blocks=blocks)
markdown_to_block_objects(markdown_text: str, expand_sections: bool | None = None) -> list[Block]
Convert markdown to editable Slack Block objects.
Parameters:
markdown_text: Markdown formatted stringexpand_sections: (Optional) Control section block expansion (same as above)
Returns:
- List of Block objects from slack_sdk that can be modified
Example:
blocks = markdown_to_block_objects("# Hello\n\nWorld")
blocks[0].text.text = "Modified Header" # Edit blocks
block_dicts = [b.to_dict() for b in blocks] # Convert when ready
Use Cases:
- Modify generated blocks before sending
- Add dynamic content to specific blocks
- Combine with custom block creation
- Inspect block structure programmatically
SlackBlocksRenderer
Main renderer class inheriting from mistletoe's BaseRenderer.
Parameters:
expand_sections: (Optional) Control section block expansion behavior
Methods:
render(document): Convert mistletoe Document to list of Block objects- Context manager support for proper resource handling
Example:
from mistletoe import Document
with SlackBlocksRenderer(expand_sections=True) as renderer:
document = Document(markdown_text)
blocks = renderer.render(document)
TableBlock
Custom table block implementation for Slack Block Kit.
Parameters:
rows: List of rows (each row is list of cell objects)block_id: Optional unique identifier (max 255 chars)column_settings: Optional column configuration
Slack Block Kit Constraints
This library automatically handles Slack's Block Kit constraints:
- Headers: Maximum 150 characters (truncated with "..." if longer)
- Text blocks: Maximum 3000 characters (truncated with "..." if longer)
- Tables: Maximum 100 rows, 20 columns per row
- Block IDs: Maximum 255 characters
Development
# Clone repository
git clone https://github.com/atacan/slack-blocks-markdown.git
cd slack-blocks-markdown
# Install with development dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Run linting
ruff check src/ tests/
black src/ tests/
# Run type checking
mypy src/
Testing
The library includes comprehensive tests covering:
- All markdown element types
- Slack constraint validation
- Edge cases and error handling
- Integration with Slack API format
- Performance with large documents
# Run with coverage
pytest --cov=slack_blocks_markdown --cov-report=html
Contributing
Contributions welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
License
MIT License - see LICENSE file for details.
Changelog
See CHANGELOG.md for version history.
Related Projects
- mistletoe - The markdown parser we use
- slack-sdk - Official Slack SDK for Python
- Slack Block Kit Builder - Visual tool for building Slack blocks
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file slack_blocks_markdown-0.2.1.tar.gz.
File metadata
- Download URL: slack_blocks_markdown-0.2.1.tar.gz
- Upload date:
- Size: 24.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c623913e0590bdc097b74697857dbf3c4b93525f6ba2100cfeb2445f1c36da7f
|
|
| MD5 |
48f2ca44f782b59e1926c3c73156d833
|
|
| BLAKE2b-256 |
afd7aa1990f8e24a38202df2d64dbaf58955f25a1eda6185e96e513ffe30cbc5
|
Provenance
The following attestation bundles were made for slack_blocks_markdown-0.2.1.tar.gz:
Publisher:
publish.yml on atacan/slack-blocks-markdown
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
slack_blocks_markdown-0.2.1.tar.gz -
Subject digest:
c623913e0590bdc097b74697857dbf3c4b93525f6ba2100cfeb2445f1c36da7f - Sigstore transparency entry: 764678186
- Sigstore integration time:
-
Permalink:
atacan/slack-blocks-markdown@f03b9ea37cf9906a619cabc0c8957f71d0f4e8cd -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/atacan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f03b9ea37cf9906a619cabc0c8957f71d0f4e8cd -
Trigger Event:
push
-
Statement type:
File details
Details for the file slack_blocks_markdown-0.2.1-py3-none-any.whl.
File metadata
- Download URL: slack_blocks_markdown-0.2.1-py3-none-any.whl
- Upload date:
- Size: 14.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
620332d365f80161c81b27cb15ae573d4c097d12768becc75219d01b35f53c3d
|
|
| MD5 |
2d02be8e518e7e82dc7bce42a68fe619
|
|
| BLAKE2b-256 |
7f28bf7fe4eb5f6a5a0c2bf673f43410335b9eb2db687c767d0ee8c875e540ba
|
Provenance
The following attestation bundles were made for slack_blocks_markdown-0.2.1-py3-none-any.whl:
Publisher:
publish.yml on atacan/slack-blocks-markdown
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
slack_blocks_markdown-0.2.1-py3-none-any.whl -
Subject digest:
620332d365f80161c81b27cb15ae573d4c097d12768becc75219d01b35f53c3d - Sigstore transparency entry: 764678209
- Sigstore integration time:
-
Permalink:
atacan/slack-blocks-markdown@f03b9ea37cf9906a619cabc0c8957f71d0f4e8cd -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/atacan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f03b9ea37cf9906a619cabc0c8957f71d0f4e8cd -
Trigger Event:
push
-
Statement type: