BlockKit for Python
You know what you want to build. A simple approval button. A form that collects feedback. A nice home tab.
But then you open docs and see this:
Three levels deep just to show text. And that's the easy part.
BlockKit fixes this. Write Python. Get perfect JSON. Ship faster.
What is this?
BlockKit for Python is a library that makes building Slack UIs actually enjoyable. Type hints tell you what's possible. Method chaining lets you build naturally. Validation catches mistakes before Slack does. Zero dependencies means it won't break your project.
Install it
See the difference
Here's how most people build Slack messages:
{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Hello from BlockKit!"
},
"accessory": {
"type": "button",
"text": {
"type": "plain_text",
"text": "Click me"
},
"action_id": "button_1"
}
}
]
}
Here's how you do it with BlockKit:
from blockkit import Message, Section, Button
message = (
Message()
.add_block(
Section("Hello from BlockKit!")
.accessory(Button("Click me").action_id("button_1"))
)
.build()
)
Same result. Half the code. No guessing.
The basics
Everything chains
Start with what you want. Add what you need. Build when you're done.
It knows what you mean
Write text. We'll figure out if it needs markdown.
# Just text? We use plain_text
Section("Hello world")
# See markdown? We switch to mrkdwn
Section("Hello *world*")
Mistakes get caught early
Forget a required field? We'll tell you before Slack does.
# This fails immediately - confirms need a deny button
Button("Delete").confirm(
Confirm()
.title("Are you sure?")
.text("This cannot be undone")
.confirm("Yes, delete it")
# Missing: .deny("Cancel")
).build()
Rules are built in
Slack has rules. Lots of them. Button text can't be too long. Sections can't have too many fields. We enforce all of them.
# This raises an error - button text has a 75 character limit
Button("This is way too long " * 10).build()
The library helps you follow the rules. You ship working code.
Real examples
A message with buttons
from blockkit import Message, Section, Actions, Button
message = (
Message()
.add_block(Section("Your order has been shipped!"))
.add_block(
Actions()
.add_element(Button("Track Package").action_id("track"))
.add_element(Button("View Details").action_id("details"))
)
.build()
)
A form that works
from blockkit import Modal, Input, PlainTextInput, Checkboxes, Option
modal = (
Modal()
.title("Create Task")
.add_block(
Input("Task Name").element(PlainTextInput().action_id("task_name"))
)
.add_block(
Input("Assignees").element(
Checkboxes()
.action_id("assignees")
.add_option(Option("Alice", "U123"))
.add_option(Option("Bob", "U456"))
)
)
.submit("Create")
.build()
)
Rich text that makes sense
from blockkit import RichText, RichTextSection, RichTextEl, RichStyle
rich_text = (
RichText()
.add_element(
RichTextSection()
.add_element(RichTextEl("Important: ").style(RichStyle().bold()))
.add_element(RichTextEl("Please review the following changes"))
)
.build()
)
A home tab users will see
from blockkit import Home, Header, Section, Divider, Context, ImageEl, Text
home = (
Home()
.add_block(Header("Welcome back!"))
.add_block(Section("Here's what's happening today:"))
.add_block(Divider())
.add_block(
Section("**5 new messages**").accessory(
Button("View All").action_id("view_messages")
)
)
.add_block(
Context()
.add_element(ImageEl("https://example.com/icon.png", "icon"))
.add_element(Text("Last updated 5 minutes ago"))
)
.build()
)
How it fits together
Every Slack UI has the same structure:
- Surfaces - The container (Message, Modal, Home)
- Blocks - The layout (Section, Actions, Input)
- Elements - The interactive bits (Button, Select, Checkboxes)
- Composition - The details (Text, Option, Confirm)
Build from the outside in. Start with a surface. Add blocks. Fill with elements.
License
MIT. Use it however you want.