Theming
The renderer ships Slack-like light and dark themes. 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 the renderer palette:
text, surfaces, borders, buttons, and charts. 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:
Nightly build
Suite duration by shard
main · 3 checks passedView 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"
}
]
}
]
}Nightly build
Suite duration by shard
main · 3 checks passedView 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 layered frameworks such as Tailwind so the CSS layer
order is correct. Unlayered host selectors can override layered library styles
regardless of import order; scope those rules away from
[data-block-kit-root] when visual fidelity matters.
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:
| Attribute | Value |
|---|---|
data-block-kit-root | Present 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.