Skip to content

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:

{
  "type": "section",
  "text": {
    "type": "mrkdwn",
    "text": "..."
  }
}

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

pip install blockkit

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.

button = (
    Button("Save")
    .action_id("save_button")
    .style(Button.PRIMARY)
    .build()
)

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:

  1. Surfaces - The container (Message, Modal, Home)
  2. Blocks - The layout (Section, Actions, Input)
  3. Elements - The interactive bits (Button, Select, Checkboxes)
  4. 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.