Validation
Rendering is deliberately tolerant; validation is deliberately strict. The
validators check a payload against the package's strict contract (limits,
required fields, element placement, nesting, and surface rules) and throw a
BlockKitInvariantError pointing at the first violation. They run without
React, so they work in Node scripts, tests, and server code:
import { assertBlockKitData, parseBlockKitData } from "react-blockkit";
const unknownPayload: unknown = { blocks: [] };
const json = '{"blocks":[]}';
assertBlockKitData(unknownPayload, "message");
const typed = parseBlockKitData(json, "message");Functions
| Function | Signature | Use |
|---|---|---|
parseBlockKitData | (input: BlockKitInput, surface?: BlockKitSurface) => BlockKitData | Parse a JSON string or validate a block-shaped input; returns the typed payload. For unknown values, use assertBlockKitData |
assertBlockKitData | (value: unknown, surface?, path?) => asserts value is BlockKitData | Validate a full payload in place |
assertRenderableBlock | (value: unknown, surface?, path?) => asserts value is RenderableBlock | Validate a single block of any type |
assertBlockType | (value: unknown, expected: RenderableBlock["type"], surface?, path?) => void | Validate a block and require a specific type |
assertRichTextBlockData | (value: unknown, path?) => void | Validate one rich_text block |
assertTextObjectData | (value: unknown, path?, max?) => void | Validate a TextObject with an optional length limit |
assertImageElementData | (value: unknown, path?) => void | Validate an image element |
assertInteractiveElementData | (value: unknown, path?) => void | Validate an interactive element |
blocksOf | (data: BlockKitData) => readonly RenderableBlock[] | Normalize { blocks } or a bare array to the block list |
surface defaults to "message" and path to a sensible root ("data",
"block", "element", "text"). The path parameter sets both error.path
and the "<path>:" prefix in error.message. Pass your own when validating a
fragment of a larger document. This function argument is separate from
<BlockKit surface>: rendering with that prop never invokes a validator.
What gets checked
- Message/view block-count limits and duplicate
block_iddetection - Required fields, type discriminators, string lengths, and per-block element limits
- Legal accessory, input, and action element placement
- Rich-text nesting and preformatted/list constraints
- Table shape, cell types, row/column limits, and aggregate text budgets
- Data-table header, row width, page size, and row-header invariants
- Chart category/series/point consistency
- Card, carousel, container, task-card, and plan constraints
- The validator's surface rules, listed in full below
Validator surface rules
The validator applies surface restrictions to thirteen block types and throws
surface_mismatch when one appears outside the listed surface:
| Validator accepts on | Blocks |
|---|---|
message only | card, carousel, container, context_actions, data_table, data_visualization, file, markdown, plan, table, task_card |
modal only | alert |
modal or home | input |
Known compatibility limitation: Slack and the renderer support input blocks
in messages, but the current validator rejects that combination with
surface_mismatch. Do not treat that rejection as evidence that Slack rejects
the payload. Every other block validates on all three surfaces. Rendering only
records the selected surface; see
Rendering messages.
BlockKitInvariantError
Every validation failure throws this error class, as does the renderer's own malformed-JSON case:
import { isBlockKitInvariantError, parseBlockKitData } from "react-blockkit";
const payload = {
blocks: [
{ type: "divider", block_id: "same" },
{ type: "divider", block_id: "same" },
],
} as const;
try {
parseBlockKitData(payload, "message");
} catch (error) {
if (isBlockKitInvariantError(error)) {
error.code; // "duplicate_id"
error.path; // "data.blocks[1].block_id"
error.message; // 'data.blocks[1].block_id: "same" is already used in this payload'
}
}Prefer isBlockKitInvariantError(error) to instanceof. The package publishes
separate ESM and CommonJS class copies, so instanceof can be false when an
error crosses that build boundary. The exported guard recognizes either copy.
| Property | Type | Meaning |
|---|---|---|
code | BlockKitInvariantCode | Stable machine-readable failure category |
path | string | JSON path to the offending value, in Slack payload notation |
message | string | "<path>: <description>" |
Error codes
| Code | Raised when |
|---|---|
invalid_json | String input fails JSON.parse |
invalid_type | A value has the wrong JavaScript type |
missing_field | A required field is absent |
invalid_value | A value is present but illegal (empty string, bad discriminator, out-of-range number) |
limit_exceeded | A Slack length, count, or size limit is exceeded |
surface_mismatch | A block is not allowed on the validated surface |
duplicate_id | Two blocks share a block_id |
unsupported_block | A block type is not in the supported inventory |
unsupported_element | An element type is not legal in its position |