Build Slack messages in Python or TypeScript — without writing JSON by hand.
Anyone who has built a non-trivial Slack message knows the drill: a wall of nested
Block Kit JSON, five levels deep, where a typo'd
field name or an over-long string sails silently through your code and only blows up
when Slack rejects the API call. slackblocks replaces that JSON with typed objects
that assemble it for you — and that complain at construction time, in your editor and
your tests, rather than in production.
- Concise —
SectionBlock("Hello, *world*!")/sectionBlock({ text: "Hello, *world*!" })instead of a ten-line JSON object. - Validated up front — character limits, required fields, mutually-exclusive options, and element-type restrictions are enforced when you construct the block, so you find out before hitting Slack's API.
- Typed — full type hints and
py.typedin Python; strict types in TypeScript that reject typo'd properties at compile time. - Plays well with the official SDKs — unpack a
Messagestraight intoclient.chat_postMessage(**message)withslack-sdk, or pass the payload directly to@slack/web-api'schat.postMessage. - One library, two languages — the same blocks, the same validation rules, and the same version numbers in both packages, so a team can hop between a Python service and a TypeScript bot without relearning anything. A shared conformance corpus keeps both implementations emitting the same Slack JSON.
- Everything Block Kit ships today — all current blocks and elements, rich text, modals and Home tabs, and the 2025 block families (tables, cards, carousels, charts).
- Light — zero runtime dependencies in Python; a single self-contained ESM module on npm.
Python (3.10+ — earlier Pythons should pin the 1.x line, see
Compatibility):
pip install slackblocksTypeScript / JavaScript (Node 20.19+ or 22.12+, ESM):
npm install @nicklambourne/slackblocksA CI notification, in Python:
from slackblocks import (
ActionsBlock,
Button,
DividerBlock,
HeaderBlock,
Message,
SectionBlock,
)
message = Message(
channel="#general",
text="Build #482 passed", # plain-text fallback for notifications
blocks=[
HeaderBlock("Build #482 passed :white_check_mark:"),
SectionBlock(
fields=[
"*Branch*\n`main`",
"*Author*\n@nick",
"*Duration*\n3m 12s",
"*Tests*\n1,247 passed",
],
),
DividerBlock(),
ActionsBlock(
elements=[
Button(text="View build", action_id="view", url="https://ci.example.com/482"),
Button(text="Re-run", action_id="rerun", value="482", style="primary"),
],
),
],
)Send it in one line with the official Slack SDK — the ** operator unpacks
Message objects directly into the call, no to_dict() boilerplate:
import os
from slack_sdk import WebClient
client = WebClient(token=os.environ["SLACK_API_TOKEN"])
client.chat_postMessage(**message)The same message in TypeScript:
import {
actionsBlock,
button,
dividerBlock,
headerBlock,
message,
sectionBlock,
} from "@nicklambourne/slackblocks";
const payload = message({
channel: "#general",
text: "Build #482 passed", // plain-text fallback for notifications
blocks: [
headerBlock({ text: "Build #482 passed :white_check_mark:" }),
sectionBlock({
fields: ["*Branch*\n`main`", "*Author*\n@nick", "*Duration*\n3m 12s", "*Tests*\n1,247 passed"],
}),
dividerBlock(),
actionsBlock({
elements: [
button({ text: "View build", actionId: "view", url: "https://ci.example.com/482" }),
button({ text: "Re-run", actionId: "rerun", value: "482", style: "primary" }),
],
}),
],
});import { WebClient } from "@slack/web-api";
const client = new WebClient(process.env.SLACK_API_TOKEN);
await client.chat.postMessage(payload);- Full docs: https://nicklambourne.github.io/slackblocks/
- Installation
- Using Blocks — every block type with code in both languages, the JSON it produces, and screenshots.
- Sending Messages
- Cookbook — end-to-end recipes for build notifications, approval requests, modals, and more.
- API Reference — Python and TypeScript.
- Migrating from 1.x · Troubleshooting & FAQ
- Changelogs: Python · TypeScript
python/— the established Python package (slackblockson PyPI).typescript/— the TypeScript package (@nicklambourne/slackblockson npm).spec/— the shared conformance contract: fixtures, invalid cases, limits, and capability coverage that both implementations are tested against.docs/— the Docusaurus documentation site.
slackblocks is dual-licensed under MIT and
BSD-3-Clause. Use whichever fits your project — this makes it
safe to vendor into projects under either license.
Contributions are welcome. Python development uses uv from
python/; TypeScript and the docs site use pnpm from the repository
root:
git clone https://github.com/nicklambourne/slackblocks.git
cd slackblocks/python
uv sync --group dev
uv run pytest test/unit test/conformance test/docs
cd ..
pnpm install
pnpm --filter @nicklambourne/slackblocks testFor the full development guide — testing conventions, the conformance-fixture workflow, docstring style, and the release process — see the Contributing page.
Bug reports and feature requests: https://github.com/nicklambourne/slackblocks/issues.

