| name | slack-block-kit |
| description | Build Slack Block Kit UIs for messages, modals, and Home tabs. Use when creating Slack notifications, interactive forms, bot responses, app dashboards, or any Slack UI. Covers blocks (Section, Actions, Input, Header), elements (Buttons, Selects, Date pickers), composition objects, and the slack-block-builder library. |
Slack Block Kit
Expert guide for building Slack UIs with Block Kit.
Overview
Block Kit is Slack's UI framework for creating rich, interactive messages, modals, and Home tabs. It uses a JSON-based structure with three main components:
- Blocks: Layout containers (Section, Actions, Input, Header, etc.)
- Elements: Interactive components (Buttons, Selects, Date pickers)
- Composition Objects: Text, options, and confirmations
Surfaces
Block Kit works on three surfaces:
| Surface | Max Blocks | Use Cases |
|---|---|---|
| Messages | 50 | Notifications, bot responses, channel posts |
| Modals | 100 | Forms, confirmations, multi-step workflows |
| Home tabs | 100 | App dashboards, user-specific content |
Quick Start
Simple Message
{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "New Order Received"
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Order ID:*\n#12345"
},
{
"type": "mrkdwn",
"text": "*Customer:*\nJohn Doe"
}
]
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View Order"
},
"style": "primary",
"action_id": "view_order",
"value": "12345"
}
]
}
]
}
Using slack-block-builder (JavaScript)
import { Message, Blocks, Elements } from 'slack-block-builder';
const orderNotification = ({ orderId, customer, channel }) => {
return Message({ channel, text: 'New Order Received' })
.blocks(
Blocks.Header({ text: 'New Order Received' }),
Blocks.Section()
.fields([
`*Order ID:*\n#${orderId}`,
`*Customer:*\n${customer}`
]),
Blocks.Actions()
.elements(
Elements.Button({ text: 'View Order', actionId: 'view_order' })
.primary()
.value(orderId)
)
)
.buildToObject();
};
Blocks Reference
Header Block
Displays large text for titles.
{
"type": "header",
"text": {
"type": "plain_text",
"text": "Budget Performance",
"emoji": true
}
}
Fields:
type(required): Always"header"text(required): plain_text object, max 150 charsblock_id(optional): Unique identifier, max 255 chars
Surfaces: Messages, Modals, Home tabs
Section Block
Most versatile block for text and accessories.
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Project Update*\nThe deployment is complete."
},
"accessory": {
"type": "button",
"text": {
"type": "plain_text",
"text": "View Details"
},
"action_id": "view_details"
}
}
With Fields (two-column layout):
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Status:*\nApproved"
},
{
"type": "mrkdwn",
"text": "*Created:*\nDec 15, 2025"
},
{
"type": "mrkdwn",
"text": "*Priority:*\nHigh"
},
{
"type": "mrkdwn",
"text": "*Assignee:*\n<@U123ABC>"
}
]
}
Fields:
type(required): Always"section"text(optional): Text object (mrkdwn or plain_text), max 3000 charsfields(optional): Array of text objects (max 10), each max 2000 charsaccessory(optional): One interactive elementblock_id(optional): Unique identifierexpand(optional): Boolean, expand text by default
Surfaces: Messages, Modals, Home tabs
Actions Block
Contains multiple interactive elements.
{
"type": "actions",
"block_id": "approval_actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Approve"
},
"style": "primary",
"action_id": "approve_request",
"value": "approved"
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Reject"
},
"style": "danger",
"action_id": "reject_request",
"value": "rejected"
}
]
}
Fields:
type(required): Always"actions"elements(required): Array of interactive elements (max 25)block_id(optional): Unique identifier
Surfaces: Messages, Modals, Home tabs
Input Block
Collects user input (modals and Home tabs primarily).
{
"type": "input",
"block_id": "description_input",
"label": {
"type": "plain_text",
"text": "Description"
},
"element": {
"type": "plain_text_input",
"action_id": "description_value",
"multiline": true,
"placeholder": {
"type": "plain_text",
"text": "Enter a description..."
}
},
"optional": true,
"hint": {
"type": "plain_text",
"text": "Provide additional context"
}
}
Fields:
type(required): Always"input"element(required): Input element (text input, select, date picker, etc.)label(required): plain_text object, max 2000 charsblock_id(optional): Unique identifierhint(optional): plain_text object, max 2000 charsoptional(optional): Boolean, default falsedispatch_action(optional): Boolean, dispatch block_actions on change
Surfaces: Modals, Messages, Home tabs
Context Block
Displays secondary, contextual information.
{
"type": "context",
"elements": [
{
"type": "image",
"image_url": "https://example.com/avatar.png",
"alt_text": "User avatar"
},
{
"type": "mrkdwn",
"text": "Posted by <@U123ABC> on Dec 15, 2025"
}
]
}
Fields:
type(required): Always"context"elements(required): Array of image/text elements (max 10)block_id(optional): Unique identifier
Surfaces: Messages, Modals, Home tabs
Divider Block
Visual separator between blocks.
{
"type": "divider"
}
Surfaces: Messages, Modals, Home tabs
Image Block
Displays an image.
{
"type": "image",
"image_url": "https://example.com/chart.png",
"alt_text": "Sales chart",
"title": {
"type": "plain_text",
"text": "Q4 Sales Performance"
}
}
Fields:
type(required): Always"image"image_url(required*): Public URL, max 3000 charsslack_file(required*): Alternative to image_urlalt_text(required): Description, max 2000 charstitle(optional): plain_text object, max 2000 charsblock_id(optional): Unique identifier
*One of image_url or slack_file is required
Surfaces: Messages, Modals, Home tabs
Markdown Block
Displays formatted markdown (messages only).
{
"type": "markdown",
"text": "**Important:** This is *formatted* text with `code`."
}
Surfaces: Messages only
Rich Text Block
Structured text representation.
{
"type": "rich_text",
"elements": [
{
"type": "rich_text_section",
"elements": [
{
"type": "text",
"text": "Hello ",
"style": {
"bold": true
}
},
{
"type": "user",
"user_id": "U123ABC"
}
]
}
]
}
Surfaces: Messages, Modals, Home tabs
Video Block
Embeds a video player.
{
"type": "video",
"title": {
"type": "plain_text",
"text": "Product Demo"
},
"video_url": "https://example.com/video.mp4",
"thumbnail_url": "https://example.com/thumb.png",
"alt_text": "Product demonstration video"
}
Surfaces: Messages, Modals, Home tabs
Elements Reference
Button Element
Interactive button for actions.
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Click Me",
"emoji": true
},
"action_id": "button_click",
"value": "button_value",
"style": "primary"
}
Fields:
type(required): Always"button"text(required): plain_text object, max 75 charsaction_id(optional): Identifier for interaction, max 255 charsvalue(optional): Payload value, max 2000 charsstyle(optional):"primary"(green) or"danger"(red)url(optional): URL to open, max 3000 charsconfirm(optional): Confirmation dialogaccessibility_label(optional): Screen reader text, max 75 chars
Works with: Section (accessory), Actions
Static Select Menu
Dropdown with predefined options.
{
"type": "static_select",
"action_id": "priority_select",
"placeholder": {
"type": "plain_text",
"text": "Select priority"
},
"options": [
{
"text": {
"type": "plain_text",
"text": "High"
},
"value": "high"
},
{
"text": {
"type": "plain_text",
"text": "Medium"
},
"value": "medium"
},
{
"text": {
"type": "plain_text",
"text": "Low"
},
"value": "low"
}
],
"initial_option": {
"text": {
"type": "plain_text",
"text": "Medium"
},
"value": "medium"
}
}
Fields:
type(required):"static_select"action_id(optional): Interaction identifieroptions(required*): Array of option objects (max 100)option_groups(required*): Grouped options (max 100 groups)initial_option(optional): Pre-selected optionplaceholder(optional): Placeholder text, max 150 charsconfirm(optional): Confirmation dialogfocus_on_load(optional): Auto-focus in modals
*One of options or option_groups required
External Select Menu
Dropdown with dynamically loaded options.
{
"type": "external_select",
"action_id": "customer_select",
"placeholder": {
"type": "plain_text",
"text": "Search customers..."
},
"min_query_length": 2
}
Fields:
type(required):"external_select"action_id(optional): Interaction identifiermin_query_length(optional): Min chars before query (default 3)initial_option(optional): Pre-selected optionplaceholder(optional): Placeholder text
Users Select Menu
Select workspace users.
{
"type": "users_select",
"action_id": "assignee_select",
"placeholder": {
"type": "plain_text",
"text": "Select assignee"
},
"initial_user": "U123ABC"
}
Channels Select Menu
Select public channels.
{
"type": "channels_select",
"action_id": "channel_select",
"placeholder": {
"type": "plain_text",
"text": "Select channel"
},
"initial_channel": "C123ABC"
}
Conversations Select Menu
Select any conversation (channels, DMs, groups).
{
"type": "conversations_select",
"action_id": "conversation_select",
"placeholder": {
"type": "plain_text",
"text": "Select conversation"
},
"default_to_current_conversation": true
}
Multi-Select Menus
All select menus have multi-select variants:
multi_static_selectmulti_external_selectmulti_users_selectmulti_channels_selectmulti_conversations_select
{
"type": "multi_users_select",
"action_id": "team_select",
"placeholder": {
"type": "plain_text",
"text": "Select team members"
},
"max_selected_items": 5
}
Date Picker
Select a date.
{
"type": "datepicker",
"action_id": "due_date",
"placeholder": {
"type": "plain_text",
"text": "Select due date"
},
"initial_date": "2025-12-31"
}
Time Picker
Select a time.
{
"type": "timepicker",
"action_id": "meeting_time",
"placeholder": {
"type": "plain_text",
"text": "Select time"
},
"initial_time": "14:30"
}
Datetime Picker
Select date and time together.
{
"type": "datetimepicker",
"action_id": "event_datetime",
"initial_date_time": 1734307200
}
Checkboxes
Multiple selection from options.
{
"type": "checkboxes",
"action_id": "features_select",
"options": [
{
"text": {
"type": "plain_text",
"text": "Email notifications"
},
"value": "email",
"description": {
"type": "mrkdwn",
"text": "*Receive daily summary*"
}
},
{
"text": {
"type": "plain_text",
"text": "Slack notifications"
},
"value": "slack"
}
],
"initial_options": [
{
"text": {
"type": "plain_text",
"text": "Email notifications"
},
"value": "email"
}
]
}
Radio Buttons
Single selection from options.
{
"type": "radio_buttons",
"action_id": "urgency_select",
"options": [
{
"text": {
"type": "plain_text",
"text": "Urgent"
},
"value": "urgent"
},
{
"text": {
"type": "plain_text",
"text": "Standard"
},
"value": "standard"
}
],
"initial_option": {
"text": {
"type": "plain_text",
"text": "Standard"
},
"value": "standard"
}
}
Plain Text Input
Single or multi-line text input.
{
"type": "plain_text_input",
"action_id": "comment_input",
"placeholder": {
"type": "plain_text",
"text": "Enter your comment..."
},
"multiline": true,
"min_length": 10,
"max_length": 500
}
Number Input
Numeric input with validation.
{
"type": "number_input",
"action_id": "quantity_input",
"is_decimal_allowed": false,
"min_value": "1",
"max_value": "100",
"placeholder": {
"type": "plain_text",
"text": "Enter quantity"
}
}
URL Input
URL input with validation.
{
"type": "url_text_input",
"action_id": "website_input",
"placeholder": {
"type": "plain_text",
"text": "https://example.com"
}
}
Email Input
Email input with validation.
{
"type": "email_text_input",
"action_id": "email_input",
"placeholder": {
"type": "plain_text",
"text": "you@example.com"
}
}
Rich Text Input
Formatted text input.
{
"type": "rich_text_input",
"action_id": "content_input",
"placeholder": {
"type": "plain_text",
"text": "Write your content..."
}
}
File Input
File upload (modals only).
{
"type": "file_input",
"action_id": "attachment_input",
"filetypes": ["pdf", "png", "jpg"],
"max_files": 3
}
Overflow Menu
Compact menu for secondary actions.
{
"type": "overflow",
"action_id": "more_actions",
"options": [
{
"text": {
"type": "plain_text",
"text": "Edit"
},
"value": "edit"
},
{
"text": {
"type": "plain_text",
"text": "Delete"
},
"value": "delete"
}
]
}
Composition Objects
Text Object
Used throughout Block Kit for text content.
Plain Text:
{
"type": "plain_text",
"text": "Hello, world!",
"emoji": true
}
Markdown (mrkdwn):
{
"type": "mrkdwn",
"text": "*Bold* _italic_ ~strike~ `code` <https://example.com|link>"
}
Confirmation Dialog
Confirmation before destructive actions.
{
"title": {
"type": "plain_text",
"text": "Confirm Delete"
},
"text": {
"type": "mrkdwn",
"text": "Are you sure you want to delete this item? This cannot be undone."
},
"confirm": {
"type": "plain_text",
"text": "Delete"
},
"deny": {
"type": "plain_text",
"text": "Cancel"
},
"style": "danger"
}
Option Object
For select menus, checkboxes, and radio buttons.
{
"text": {
"type": "plain_text",
"text": "Option Label"
},
"value": "option_value",
"description": {
"type": "plain_text",
"text": "Optional description"
},
"url": "https://example.com"
}
Option Group
Grouped options for select menus.
{
"label": {
"type": "plain_text",
"text": "Group Label"
},
"options": [
{
"text": {
"type": "plain_text",
"text": "Option 1"
},
"value": "option_1"
}
]
}
Modal Reference
Opening a Modal
{
"type": "modal",
"callback_id": "feedback_modal",
"title": {
"type": "plain_text",
"text": "Submit Feedback"
},
"submit": {
"type": "plain_text",
"text": "Submit"
},
"close": {
"type": "plain_text",
"text": "Cancel"
},
"blocks": [
{
"type": "input",
"block_id": "feedback_type",
"label": {
"type": "plain_text",
"text": "Feedback Type"
},
"element": {
"type": "static_select",
"action_id": "type_select",
"options": [
{
"text": {
"type": "plain_text",
"text": "Bug Report"
},
"value": "bug"
},
{
"text": {
"type": "plain_text",
"text": "Feature Request"
},
"value": "feature"
}
]
}
},
{
"type": "input",
"block_id": "feedback_content",
"label": {
"type": "plain_text",
"text": "Description"
},
"element": {
"type": "plain_text_input",
"action_id": "content_input",
"multiline": true
}
}
]
}
Modal Fields
type(required): Always"modal"title(required): plain_text, max 24 charsblocks(required): Array of blocks (max 100)callback_id(optional): Identifier for view_submissionsubmit(optional): Submit button text, max 24 charsclose(optional): Close button text, max 24 charsprivate_metadata(optional): String passed to submission, max 3000 charsclear_on_close(optional): Clear all views on closenotify_on_close(optional): Send view_closed eventexternal_id(optional): Custom identifiersubmit_disabled(optional): Disable submit button initially
slack-block-builder Library
Installation
npm install slack-block-builder
Imports
import {
Message,
Modal,
HomeTab,
Blocks,
Elements,
Bits,
Md
} from 'slack-block-builder';
Building Messages
const message = Message({ channel: 'C123ABC', text: 'Fallback text' })
.blocks(
Blocks.Header({ text: 'Welcome!' }),
Blocks.Section({ text: 'Hello, *world*!' }),
Blocks.Divider(),
Blocks.Actions()
.elements(
Elements.Button({ text: 'Click Me', actionId: 'click' })
.primary()
)
)
.buildToObject();
Building Modals
const modal = Modal({ title: 'My Form', callbackId: 'form_submit' })
.submit('Save')
.close('Cancel')
.blocks(
Blocks.Input({ label: 'Name', blockId: 'name_block' })
.element(
Elements.TextInput({ actionId: 'name_input' })
.placeholder('Enter your name')
),
Blocks.Input({ label: 'Priority', blockId: 'priority_block' })
.element(
Elements.StaticSelect({ actionId: 'priority_select' })
.options(
Bits.Option({ text: 'High', value: 'high' }),
Bits.Option({ text: 'Medium', value: 'medium' }),
Bits.Option({ text: 'Low', value: 'low' })
)
)
)
.buildToObject();
Markdown Helpers
import { Md } from 'slack-block-builder';
Md.bold('text') // *text*
Md.italic('text') // _text_
Md.strike('text') // ~text~
Md.code('text') // `text`
Md.codeBlock('text') // ```text```
Md.link('url', 'text') // <url|text>
Md.user('U123') // <@U123>
Md.channel('C123') // <#C123>
Md.emoji('smile') // :smile:
Md.listBullet(['a', 'b']) // • a\n• b
Build Methods
// Returns JavaScript object
modal.buildToObject();
// Returns JSON string
modal.buildToJSON();
// Returns only blocks array
modal.getBlocks();
Best Practices
Naming Conventions
action_id:{verb}_{noun}- e.g.,submit_form,select_priorityblock_id:{noun}_block- e.g.,name_block,options_blockcallback_id:{feature}_{action}- e.g.,feedback_submit
Performance
- Keep messages under 50 blocks
- Keep modals under 100 blocks
- Use external_select for large option lists (>100 items)
- Minimize image usage in high-traffic messages
Accessibility
- Always provide
alt_textfor images - Use
accessibility_labelfor buttons with icons - Ensure sufficient color contrast
- Provide clear, descriptive labels
Mobile Considerations
- Use shorter text (truncation occurs ~30 chars for buttons)
- Limit fields in Section blocks (2-3 max)
- Test in mobile Slack client
- Avoid complex nested layouts
Common Patterns
Notification with Actions
Message({ channel, text: 'New request' })
.blocks(
Blocks.Header({ text: 'New Request' }),
Blocks.Section()
.fields([
`*From:*\n${requester}`,
`*Priority:*\n${priority}`
]),
Blocks.Context()
.elements([`Submitted ${timestamp}`]),
Blocks.Divider(),
Blocks.Actions()
.elements(
Elements.Button({ text: 'Approve', actionId: 'approve' }).primary(),
Elements.Button({ text: 'Reject', actionId: 'reject' }).danger()
)
)
.buildToObject();
Form Modal with Validation
Modal({ title: 'Create Task', callbackId: 'task_create' })
.submit('Create')
.close('Cancel')
.blocks(
Blocks.Input({ label: 'Task Name', blockId: 'name' })
.element(Elements.TextInput({ actionId: 'name_input' })),
Blocks.Input({ label: 'Assignee', blockId: 'assignee' })
.element(Elements.UsersSelect({ actionId: 'assignee_select' })),
Blocks.Input({ label: 'Due Date', blockId: 'due_date' })
.element(Elements.DatePicker({ actionId: 'date_select' }))
.optional(true)
)
.buildToObject();
Home Tab Dashboard
HomeTab()
.blocks(
Blocks.Header({ text: `Welcome, ${userName}!` }),
Blocks.Section({ text: 'Here are your pending tasks:' }),
Blocks.Divider(),
...tasks.map(task =>
Blocks.Section({ text: `• ${task.title}` })
.accessory(
Elements.Button({ text: 'Complete', actionId: `complete_${task.id}` })
)
),
Blocks.Divider(),
Blocks.Actions()
.elements(
Elements.Button({ text: 'New Task', actionId: 'new_task' }).primary()
)
)
.buildToObject();
Troubleshooting
Common Errors
| Error | Cause | Solution |
|---|---|---|
invalid_blocks |
Malformed JSON | Validate in Block Kit Builder |
too_many_blocks |
Exceeds limit | Reduce to 50 (msg) or 100 (modal) |
invalid_element |
Wrong element in block | Check element compatibility |
missing_text |
Required text missing | Add text or fields to Section |
Validation Tips
- Use Block Kit Builder to test JSON
- Check element compatibility with blocks
- Verify required fields are present
- Test on mobile for display issues
Resources
Reference Documentation
For comprehensive guides, see the references/ directory:
- blocks.md - Detailed documentation on all block types
- interactive-components.md - Complete guide to interactive elements
- layout-patterns.md - Composition and nesting best practices
- surfaces.md - Messages, modals, and home tabs deep dive