Skip to content

Repository files navigation

slackblocks

Licence: MIT Licence: BSD-3-Clause Python Versions PyPI npm Go Maven Central NuGet RubyGems Downloads Python CI TypeScript CI Go CI Java CI .NET CI Ruby CI Docs

Build Slack messages in Python, TypeScript, Go, Java, C#, or Ruby — 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.

Why slackblocks?

  • Concise — SectionBlock("Hello, *world*!") / SectionBlock.builder().markdownText("Hello, *world*!").build() / new 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.typed in Python, strict types in TypeScript, compile-checked concrete fluent builders in Go and Java, and typed constructors with nullable annotations in C#, and RBS signatures in Ruby.
  • Plays well with established Slack clients — unpack a Message straight into client.chat_postMessage(**message) with slack-sdk, pass a payload directly to @slack/web-api, pass Go block builders directly to slack-go/slack, pass Java blocks directly to the official Slack Java SDK, serialize C# values with System.Text.Json, or call to_json on Ruby values.
  • One library, six languages — the same blocks, validation rules, and version numbers in Python, TypeScript, Go, Java, C#, and Ruby. A shared conformance corpus keeps all six 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 self-contained ESM module on npm, one direct Go dependency (slack-go/slack), Java integration through the official Slack model interfaces, no dependencies beyond .NET itself in C#, and only Ruby's default JSON gem in Ruby.

Installation

Python (3.10+ — earlier Pythons should pin the 1.x line, see Compatibility):

pip install slackblocks

TypeScript / JavaScript (Node 20.19+ or 22.12+, ESM):

npm install @nicklambourne/slackblocks

Go (1.22+):

go get github.com/nicklambourne/slackblocks/go/v2

Java (17+):

<dependency>
  <groupId>io.github.nicklambourne</groupId>
  <artifactId>slackblocks</artifactId>
  <version>2.5.0</version>
</dependency>

C# (.NET 8+):

dotnet add package Slackblocks

Ruby (3.3+):

gem install slackblocks

Quickstart

A 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"),
    ),
  )
  .build();
import { WebClient } from "@slack/web-api";

const client = new WebClient(process.env.SLACK_API_TOKEN);
await client.chat.postMessage(payload);

And in Go:

client := slack.New(os.Getenv("SLACK_API_TOKEN"))
_, _, err := client.PostMessageContext(
    context.Background(),
    "C0123456",
    slack.MsgOptionText("Build #482 passed", false),
    slack.MsgOptionBlocks(
        slackblocks.NewHeaderBlock().Text("Build #482 passed :white_check_mark:"),
        slackblocks.NewSectionBlock().Fields(
            "*Branch*\n`main`",
            "*Tests*\n1,247 passed",
        ),
        slackblocks.NewDividerBlock(),
    ),
)

And in Java:

SectionBlock block = SectionBlock.builder()
    .markdownText("Build #482 passed :white_check_mark:")
    .build();

var client = Slack.getInstance().methods(System.getenv("SLACK_API_TOKEN"));
client.chatPostMessage(ChatPostMessageRequest.builder()
    .channel("C0123456")
    .text("Build #482 passed")
    .blocks(List.of(block))
    .build());

And in C#:

using Slackblocks.Blocks;
using Slackblocks.Elements;
using Slackblocks.Payloads;

var message = new MessagePayload(
    "C0123456",
    text: "Build #482 passed", // plain-text fallback for notifications
    blocks:
    [
        new HeaderBlock("Build #482 passed :white_check_mark:"),
        new SectionBlock(fields: ["*Branch*\n`main`", "*Tests*\n1,247 passed"]),
        new DividerBlock(),
        new ActionsBlock([new ButtonElement("View build", "view", url: "https://ci.example.com/482")]),
    ]);

// POST message.ToJson() to chat.postMessage with HttpClient and a bot token.

And in Ruby:

require "slackblocks"

message = Slackblocks::MessagePayload.new(
  channel: "#general",
  text: "Build #482 passed", # plain-text fallback for notifications
  blocks: [
    Slackblocks::HeaderBlock.new(text: "Build #482 passed :white_check_mark:"),
    Slackblocks::SectionBlock.new(
      fields: [
        "*Branch*\n`main`",
        "*Author*\n@nick",
        "*Duration*\n3m 12s",
        "*Tests*\n1,247 passed"
      ]
    ),
    Slackblocks::DividerBlock.new,
    Slackblocks::ActionsBlock.new(
      elements: [
        Slackblocks::ButtonElement.new(
          text: "View build", action_id: "view", url: "https://ci.example.com/482"
        ),
        Slackblocks::ButtonElement.new(
          text: "Re-run", action_id: "rerun", value: "482", style: :primary
        )
      ]
    )
  ]
)

puts message.to_json

The build notification rendered in Slack

Documentation

Repository layout

  • python/ — the established Python package (slackblocks on PyPI).
  • typescript/ — the TypeScript package (@nicklambourne/slackblocks on npm).
  • go/ — the Go v2 module (github.com/nicklambourne/slackblocks/go/v2).
  • java/ — the Java artifact (io.github.nicklambourne:slackblocks on Maven Central).
  • csharp/ — the .NET package (Slackblocks on NuGet).
  • ruby/ — the Ruby gem (slackblocks on RubyGems from 2.5.0).
  • spec/ — the shared conformance contract: fixtures, invalid cases, limits, and capability coverage that all six implementations are tested against.
  • docs/ — the Docusaurus documentation site.

Licensing

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.

Contributing

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 test

cd go
go test -race -cover ./...

cd ../java
./mvnw clean verify

cd ../csharp
dotnet test

cd ../ruby
bundle install
bundle exec ruby -I test -e 'Dir[File.expand_path("test/*_test.rb")].sort.each { |path| require path }'

For the full development guide — testing conventions, the conformance-fixture workflow, docstring style, and the release process — see the Contributing page.

Adding a newly released Slack component or field? Follow the in-repo Adding New Block Kit Features for the shared contract, implementation, test, and documentation checklist.

Adding a new language? Follow the in-repo language implementation guide for native API design, parity, testing, documentation, packaging and release integration.

Bug reports and feature requests: https://github.com/nicklambourne/slackblocks/issues.

About

🎲 Native language APIs for building messages using the Slack Block Kit API

Topics

Resources

Stars

78 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages