Handling actions
Buttons, selects, overflow menus, checkboxes, radio buttons, inputs, and the
context_actions elements all report user interaction through a single
onAction callback:
import { BlockKit, type BlockKitAction, type BlockKitInput } from "react-blockkit";
export function Preview({ data }: { data: BlockKitInput }) {
return (
<BlockKit
data={data}
onAction={(action: BlockKitAction) => {
console.log(action.actionId, action.value);
}}
/>
);
}BlockKitAction is react-blockkit's own local event shape, not the
interactivity payload Slack POSTs to an app. Nothing is sent to Slack.
onAction hands you the interaction and your app decides what to do with it, whether that
is updating local state or calling your own backend.
The BlockKitAction shape
| Field | Type | Meaning |
|---|---|---|
actionId | string | The element's action_id |
type | string | The element's type ("button", "static_select", …) |
blockId | string | undefined | The enclosing block's block_id, when set |
value | string | undefined | Single value (button value, selected option, input text) |
values | readonly string[] | undefined | Multiple values (multi-selects, checked checkboxes) |
Elements without an action_id never emit, so the callback fires only when there is an ID
to report.
What each element emits
| Element | Fires on | Payload |
|---|---|---|
| Button | Click | value when the button declares one; URL buttons render as links and still emit |
| Overflow menu | Click | actionId and type only; the ellipsis button opens no menu, so no option is reported |
| Select (all variants) | Selection change | value, or values for multi_* variants |
| Checkboxes | Toggle | values: every currently checked option |
| Radio buttons | Selection | value of the chosen option |
| Text/email/URL/number inputs | Each change | value: the current text |
| Rich text input | Each change | value: the editor's plain text |
| File input | Selection | value: the first selected file's name |
| Date picker | Click | value: the initial_date, or an empty string (renders as a button) |
| Time and datetime pickers | Each change | value: the control's current value |
| Icon button | Click | value when the element declares one |
| Feedback buttons | Click | value from the pressed button's positive_button.value or negative_button.value |
Try it
Interact with the message below; the local BlockKitAction each element
emits appears under the render.
View payload JSON
{
"blocks": [
{
"type": "section",
"block_id": "review",
"text": {
"type": "mrkdwn",
"text": "*PR #482* is ready for review."
},
"accessory": {
"type": "static_select",
"action_id": "assign_reviewer",
"placeholder": {
"type": "plain_text",
"text": "Assign reviewer"
},
"options": [
{
"text": {
"type": "plain_text",
"text": "Maya"
},
"value": "maya"
},
{
"text": {
"type": "plain_text",
"text": "Sam"
},
"value": "sam"
}
]
}
},
{
"type": "actions",
"block_id": "decision",
"elements": [
{
"type": "button",
"action_id": "approve",
"text": {
"type": "plain_text",
"text": "Approve"
},
"style": "primary",
"value": "pr-482"
},
{
"type": "button",
"action_id": "request_changes",
"text": {
"type": "plain_text",
"text": "Request changes"
}
}
]
}
]
}Composed blocks
When rendering blocks individually, put onAction on BlockKitProvider, the
same context <BlockKit /> uses internally:
import {
ActionsBlock,
BlockKitProvider,
type ActionsBlockData,
type BlockKitAction,
} from "react-blockkit";
export function record(action: BlockKitAction) {
console.log(action);
}
export function Preview({ actionsBlock }: { actionsBlock: ActionsBlockData }) {
return (
<BlockKitProvider onAction={record}>
<ActionsBlock block={actionsBlock} />
</BlockKitProvider>
);
}