react-blockkit
v0.6.0
Published
React components for previewing Slack Block Kit JSON in light and dark themes.
Maintainers
Readme
react-blockkit
React components for previewing Slack Block Kit JSON in light and dark themes. Known Slack blocks render as Slack-like UI; unknown block and element discriminators stay visible as labeled fallbacks. Rendering is deliberately tolerant, while the separate validation API enforces the package's strict payload contract.
react-blockkit is not coupled to a Markdown compiler. To compile Markdown
into Block Kit first, use
slackmark.
Install
pnpm add react-blockkit react react-domImport the precompiled stylesheet once from your application's global entry point or root layout:
import "react-blockkit/styles.css";Consumers do not need a StyleX plugin.
The JavaScript entry does not install styles at runtime. Import the stylesheet
after layered frameworks such as Tailwind so the layer order is correct.
Unlayered host rules can override layered library CSS regardless of import
order; scope those rules away from [data-block-kit-root] when visual fidelity
matters.
Render typed Block Kit
import { BlockKit, type BlockKitData } from "react-blockkit";
const data = {
blocks: [
{
type: "section",
text: {
type: "mrkdwn",
text: "A message with *bold text* and <https://slack.com|a link>.",
},
},
{
type: "actions",
elements: [
{
type: "button",
action_id: "approve",
text: { type: "plain_text", text: "Approve" },
style: "primary",
},
],
},
],
} satisfies BlockKitData;
export function Preview() {
return (
<BlockKit
aria-label="Approval request preview"
data={data}
onAction={(action) => {
console.log(action.actionId, action.value);
}}
/>
);
}data accepts a payload object, a bare block array, or a JSON string:
<BlockKit data={jsonFromEditor} />Rendering does not run strict validation. Unknown discriminators and malformed
known shapes render a labeled fallback or the usable fields that remain.
Malformed JSON strings throw BlockKitInvariantError with
code: "invalid_json" and path: "data".
Validate separately when you need strict payload checks:
import { assertBlockKitData } from "react-blockkit";
const payload: unknown = JSON.parse(jsonFromEditor);
assertBlockKitData(payload, "message");
// payload is now BlockKitDataSee Validation for limits, surface checks, the current message-input compatibility caveat, and portable error handling.
Compose individual blocks
Every layout block and core element is exported independently:
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>
);
}Use the standalone components with their exported …Data types. Wrap them in
BlockKitProvider when they need resolvers or onAction.
Text formats
Slack's text formats are not interchangeable:
plain_textis literal text.mrkdwntext objects use Slack's*bold*,<url|label>, mention, emoji, and date syntax.- A
markdownblock uses CommonMark and GFM, including**bold**, standard links, lists, task lists, tables, and fenced code. rich_textis already a structured element tree and is not reparsed.
The renderer also recognizes Slack mentions and emoji in ordinary Markdown text, while respecting Markdown escapes and code spans. See Markdown and mrkdwn before generating text programmatically.
Themes, surfaces, and resolvers
<BlockKit data={data} theme="dark" surface="message" />theme controls presentation. surface only sets the root's data-surface
attribute and the value exposed through BlockKitProvider; built-in blocks do
not branch on it, and it does not invoke validation. Validation helpers accept
a separate surface argument when you want the package's surface checks.
Slack IDs can be mapped to human-readable labels without changing the payload:
<BlockKit
data={data}
resolvers={{
user: (id) => users[id] ?? id,
channel: (id) => channels[id] ?? id,
emoji: (name) => emoji[name] ?? `:${name}:`,
}}
/>Resolvers may return any React node.
Local interaction callbacks
onAction reports interactions to the host application. It does not send a
Slack interactivity payload or perform network I/O. Elements emit only when
they have an action_id; workflow_button is display-only. See
Handling actions
for the exact local event shape and preview limitations.
Markdown safety fallbacks
Markdown blocks preserve their source as visible, inert text when an input exceeds any of these parser or render bounds:
- 12,000 source characters (Slack's cumulative markdown-block limit)
- 1,024 attention or strikethrough delimiter sequences
- 256 GFM table columns or 4,096 cumulative parsed table cells; the header and delimiter pair count as two rows
- 64 blockquote/list markers on one source line, counted together
- 64 nested render levels
These safety fallbacks are separate from the strict validation API.
SSR and accessibility
The renderer supports renderToString and static generation without browser
globals. Interactive controls activate after hydration. In a React Server
Components framework, render it through a client module because the package
uses React context and hooks; keep the stylesheet import in the framework's
global CSS entry. Date pickers do not read the current clock. A datetime
picker's initial HTML uses the server's local time zone, suppresses the
expected value mismatch, and updates to the viewer's local time after
hydration. Render that control after mount only when its pre-hydration value
must already use the viewer's zone.
The root is a named role="article" (default label:
"Slack Block Kit preview"). Set a specific aria-label when a page contains
more than one preview. The renderer uses native lists, tables, links, form
controls, image alternatives, and named chart images where the payload allows,
but it is a preview—not a replacement for accessibility testing of the
workflow around it. Current semantics and limitations are documented in
Accessibility.
API reference
- BlockKit and standalone components
- Blocks, elements, and text behavior
- Exported types
- Validation and errors
Development
From the Slackmark monorepo root:
pnpm --filter react-blockkit typecheck
pnpm --filter react-blockkit test
pnpm --filter react-blockkit buildThe build emits ESM, CJS, declarations, source maps, and dist/styles.css.
