Unknown blocks and fallbacks
Rendering is total for block-shaped JSON: every entry in the payload produces visible output. Slack ships new block types regularly, and payloads retrieved from the API can contain shapes the renderer has never seen. Those render as a labeled, bounded fallback instead of crashing the tree or disappearing silently.
View payload JSON
{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "The next block type does not exist yet:"
}
},
{
"type": "meeting_recap",
"summary": "Q3 planning sync",
"attendees": 14
}
]
}Degradation is not only about whole blocks. This payload clamps an out-of-range header
level, hits a slack_file image with no fetchable URL, renders a zero-total pie, leaves an
unresolved mention and an unknown emoji visible, and drops an invented accessory inside an
otherwise fine section:
Clamped to h4
Nothing to plot
View payload JSON
{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "Clamped to h4"
},
"level": 9
},
{
"type": "image",
"alt_text": "quarterly chart",
"slack_file": {
"id": "F0123456789"
}
},
{
"type": "data_visualization",
"title": "Nothing to plot",
"chart": {
"type": "pie",
"segments": [
{
"label": "none",
"value": 0
}
]
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Unresolved: <@U9999999999> :not-a-real-emoji:"
},
"accessory": {
"type": "hologram_projector",
"action_id": "beam"
}
}
]
}The fallback keeps a human-readable label in the DOM, tagged with
data-unhandled-block and data-block-type attributes so you can detect or
style degraded content:
document.querySelectorAll("[data-unhandled-block]");Fallbacks below the block level carry their own attributes:
data-unhandled-element (elements and accessories),
data-unhandled-rich-text-container and data-unhandled-rich-text-element
(rich text), and data-unhandled-chart-type (charts).
What degrades, and how
| Input | Behavior |
|---|---|
Unknown block type | Labeled fallback node with data-block-type |
| Unknown element or accessory inside a known block | The block renders; the element becomes a labeled fallback |
| Unknown rich-text container or inline element | Visible text/reference fallback inside the rich text |
Out-of-range presentation values (e.g. header.level: 9) | Clamped to the legal range |
image with a slack_file ID | Alt-labeled placeholder, because a file ID has no renderable URL |
| Unresolved mentions and custom emoji | ID or :shortcode: stays visible (see Resolvers) |
| Empty collections (no rows, no chart data) | Render safely; a zero-total pie chart shows a labeled "No chart data" state |
What throws
Only non-JSON input: passing a string that fails JSON.parse throws
BlockKitInvariantError with code: "invalid_json". The never-throw
guarantee starts once the input is valid JSON.
When you want strictness
Tolerant rendering shows the payload as-is. For Slack acceptance checks, run the validators before rendering:
import { parseBlockKitData } from "react-blockkit";
const json = '{"blocks":[{"type":"section","text":{"type":"mrkdwn","text":"hi"}}]}';
const typed = parseBlockKitData(json, "message");Validation enforces Slack's documented limits (block counts, text lengths, element placement, nesting), which the renderer deliberately does not. The full rule inventory is in the validation reference.