Theming

The renderer ships two themes, styled to match Slack's light and dark appearances. Switch with the theme prop:

import { BlockKit, type BlockKitInput } from "react-blockkit";

export function Preview({ data }: { data: BlockKitInput }) {
  return <BlockKit data={data} theme="dark" />;
}

theme is "light" (default) or "dark". It controls every color in the render: text, surfaces, borders, buttons, chart palettes. It also sets color-scheme so native form controls (selects, date pickers, scrollbars) follow along.

The same payload, light then dark. Watch the chart palette and the button fills, not just the background:

theme="light" (default)

Nightly build

Branch: main
Tests: 214 passed

Suite duration by shard

020406080123
seconds
Duration
Triggered from main · 3 checks passed
View payload JSON
{
  "blocks": [
    {
      "type": "header",
      "text": {
        "type": "plain_text",
        "text": "Nightly build"
      }
    },
    {
      "type": "section",
      "fields": [
        {
          "type": "mrkdwn",
          "text": "*Branch:*\nmain"
        },
        {
          "type": "mrkdwn",
          "text": "*Tests:*\n214 passed"
        }
      ]
    },
    {
      "type": "divider"
    },
    {
      "type": "data_visualization",
      "title": "Suite duration by shard",
      "chart": {
        "type": "bar",
        "axis_config": {
          "categories": [
            "1",
            "2",
            "3"
          ],
          "y_label": "seconds"
        },
        "series": [
          {
            "name": "Duration",
            "data": [
              {
                "label": "1",
                "value": 62
              },
              {
                "label": "2",
                "value": 48
              },
              {
                "label": "3",
                "value": 71
              }
            ]
          }
        ]
      }
    },
    {
      "type": "actions",
      "elements": [
        {
          "type": "button",
          "action_id": "promote",
          "style": "primary",
          "text": {
            "type": "plain_text",
            "text": "Promote"
          }
        },
        {
          "type": "button",
          "action_id": "revert",
          "style": "danger",
          "text": {
            "type": "plain_text",
            "text": "Revert"
          }
        },
        {
          "type": "button",
          "action_id": "logs",
          "text": {
            "type": "plain_text",
            "text": "View logs"
          }
        }
      ]
    },
    {
      "type": "context",
      "elements": [
        {
          "type": "mrkdwn",
          "text": "Triggered from `main` · 3 checks passed"
        }
      ]
    }
  ]
}
theme="dark"

Nightly build

Branch: main
Tests: 214 passed

Suite duration by shard

020406080123
seconds
Duration
Triggered from main · 3 checks passed
View payload JSON
{
  "blocks": [
    {
      "type": "header",
      "text": {
        "type": "plain_text",
        "text": "Nightly build"
      }
    },
    {
      "type": "section",
      "fields": [
        {
          "type": "mrkdwn",
          "text": "*Branch:*\nmain"
        },
        {
          "type": "mrkdwn",
          "text": "*Tests:*\n214 passed"
        }
      ]
    },
    {
      "type": "divider"
    },
    {
      "type": "data_visualization",
      "title": "Suite duration by shard",
      "chart": {
        "type": "bar",
        "axis_config": {
          "categories": [
            "1",
            "2",
            "3"
          ],
          "y_label": "seconds"
        },
        "series": [
          {
            "name": "Duration",
            "data": [
              {
                "label": "1",
                "value": 62
              },
              {
                "label": "2",
                "value": 48
              },
              {
                "label": "3",
                "value": 71
              }
            ]
          }
        ]
      }
    },
    {
      "type": "actions",
      "elements": [
        {
          "type": "button",
          "action_id": "promote",
          "style": "primary",
          "text": {
            "type": "plain_text",
            "text": "Promote"
          }
        },
        {
          "type": "button",
          "action_id": "revert",
          "style": "danger",
          "text": {
            "type": "plain_text",
            "text": "Revert"
          }
        },
        {
          "type": "button",
          "action_id": "logs",
          "text": {
            "type": "plain_text",
            "text": "View logs"
          }
        }
      ]
    },
    {
      "type": "context",
      "elements": [
        {
          "type": "mrkdwn",
          "text": "Triggered from `main` · 3 checks passed"
        }
      ]
    }
  ]
}

The root paints its own background in the active theme's message-surface color, so a dark render sits correctly on any host page.

The stylesheet

All theme variables live in the one precompiled stylesheet:

import "react-blockkit/styles.css";

Import it once, after any global reset (Tailwind preflight, normalize). The renderer's rules must come later in the cascade, or resets like button and list normalization will override them.

Styling the root

<BlockKit /> accepts a className that is appended to the root element. Use it for sizing and placement (width, margin) rather than restyling block internals:

import { BlockKit, type BlockKitInput } from "react-blockkit";

export function Preview({ data }: { data: BlockKitInput }) {
  return <BlockKit className="message-preview" data={data} />;
}

The root also carries stable data attributes for targeting from CSS or tests:

AttributeValue
data-block-kit-rootPresent on the root element
data-theme"light" or "dark"
data-surface"message", "modal", or "home"

Block components accept className the same way when composed individually.