Rendering messages
<BlockKit /> takes one required prop, data, and renders the whole payload.
What follows is what data accepts, how surfaces work, and how to compose
individual blocks when you don't want the all-in-one component.
Accepted input shapes
| Shape | Example |
|---|---|
| Payload object | { blocks: [ … ] } |
| Block array | [ { type: "section", … } ] |
| JSON string | '{"blocks":[…]}' or '[…]' |
A JSON string is parsed before rendering; malformed JSON throws
BlockKitInvariantError with code: "invalid_json". This makes <BlockKit />
safe to point directly at editor output:
import { BlockKit } from "react-blockkit";
const jsonFromEditor = '{"blocks":[{"type":"section","text":{"type":"mrkdwn","text":"hi"}}]}';
export function Preview() {
return <BlockKit data={jsonFromEditor} />;
}Once the input is block-shaped, rendering is total: every entry produces visible output, including unknown or future block types. See Unknown blocks and fallbacks for the exact degradation behavior, and the validation reference for strict Slack-acceptance checks.
A fuller message
Blocks render in payload order with Slack's spacing rhythm:
Deploy summary
v2.41.0main · 3 checks passedView payload JSON
{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "Deploy summary"
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Service:*\napi-gateway"
},
{
"type": "mrkdwn",
"text": "*Version:*\n`v2.41.0`"
},
{
"type": "mrkdwn",
"text": "*Region:*\nus-east-1"
},
{
"type": "mrkdwn",
"text": "*Status:*\n:white_check_mark: Healthy"
}
]
},
{
"type": "divider"
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": "Triggered from `main` · 3 checks passed"
}
]
}
]
}Surfaces
Slack renders blocks in three surfaces. Pass the one you are previewing:
import { BlockKit, type BlockKitInput } from "react-blockkit";
export function Preview({ data }: { data: BlockKitInput }) {
return <BlockKit data={data} surface="modal" />;
}surface is "message" (default), "modal", or "home". Rendering never
rejects a payload because of the selected surface, so a block Slack would not
accept there still renders. This payload carries an input block, which Slack
accepts only in modals and home tabs, and the message surface renders it anyway:
Deploy summary
v2.41.0 is live in us-east-1.View payload JSON
{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "Deploy summary"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*api-gateway* `v2.41.0` is live in `us-east-1`."
}
},
{
"type": "input",
"label": {
"type": "plain_text",
"text": "Rollback note"
},
"element": {
"type": "plain_text_input",
"action_id": "note"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"action_id": "confirm",
"style": "primary",
"text": {
"type": "plain_text",
"text": "Confirm"
}
}
]
}
]
}The pixels do not change when you switch surfaces. surface feeds context and validation,
never the render, so the check you want is the validator:
import { assertBlockKitData } from "react-blockkit";
// Throws BlockKitInvariantError, code "surface_mismatch", at
// data.blocks[2]: "input blocks are only legal on modal and home surfaces".
assertBlockKitData(payload, "message");Pass the same surface to
parseBlockKitData or assertBlockKitData
whenever you need strict surface compatibility. That page lists every
surface-restricted block.
Compose individual blocks
Every layout block and core element is exported as its own component. Wrap
composed blocks in BlockKitProvider so they share the same surface,
resolvers, and action handler that <BlockKit /> would provide:
import { BlockKitProvider, ButtonElement, SectionBlock } from "react-blockkit";
export function ComposedPreview() {
return (
<BlockKitProvider surface="message">
<SectionBlock
block={{
type: "section",
text: { type: "plain_text", text: "Composable block" },
}}
/>
<ButtonElement
element={{
type: "button",
action_id: "continue",
text: { type: "plain_text", text: "Continue" },
}}
/>
</BlockKitProvider>
);
}Standalone components follow the same tolerant rendering rules as
<BlockKit />: block components take the Slack object as a block prop,
elements take element, and unknown shapes degrade to visible fallbacks. The
full component list is in the
<BlockKit /> reference.
theme lives on <BlockKit />, not on BlockKitProvider, so standalone
blocks default to light styling. See
Theming.