Back to Blog
Guide

Slack Block Kit Input Block: Modal Fields, state.values and Inline Errors, Tested

We opened a modal with seven input blocks in a test workspace and captured every payload: what Slack checks before your app sees anything, the exact view_submission state.values, response_action errors, dispatch_action, and input blocks in messages.

Slack Green Team
October 7, 2026
October 7, 2026
4 min read
Share:
slack api
block kit

A Block Kit input block holds one form field, such as a plain_text_input, number_input, email_text_input or url_text_input, with a label above it. You use input blocks in modals, where Slack sends the values to your app when the user clicks Submit, and Slack also accepted them in a message. On 7 October 2026 we opened a modal with seven input blocks from a slash command in our free-plan test workspace, ran a Socket Mode app with Python slack_sdk, and logged every payload. Slack checked required fields, number ranges and email format in the browser and sent nothing to the app until they passed; it did not check the URL field.

A Slack modal titled Input block test with input blocks for Title with a 30-character counter, multiline Notes, Quantity 1 to 10, Email with a mail icon, Link with a link icon and an Optional comment field

The blocks we used

This is the blocks array of the modal, opened with views.open from the slash command's trigger_id:

[
  {
    "type": "input",
    "block_id": "title_b",
    "label": {
      "type": "plain_text",
      "text": "Title"
    },
    "element": {
      "type": "plain_text_input",
      "action_id": "title_a",
      "max_length": 30
    }
  },
  {
    "type": "input",
    "block_id": "notes_b",
    "label": {
      "type": "plain_text",
      "text": "Notes"
    },
    "element": {
      "type": "plain_text_input",
      "action_id": "notes_a",
      "multiline": true
    }
  },
  {
    "type": "input",
    "block_id": "qty_b",
    "label": {
      "type": "plain_text",
      "text": "Quantity (1-10)"
    },
    "element": {
      "type": "number_input",
      "action_id": "qty_a",
      "is_decimal_allowed": false,
      "min_value": "1",
      "max_value": "10"
    }
  },
  {
    "type": "input",
    "block_id": "email_b",
    "label": {
      "type": "plain_text",
      "text": "Email"
    },
    "element": {
      "type": "email_text_input",
      "action_id": "email_a"
    }
  },
  {
    "type": "input",
    "block_id": "url_b",
    "label": {
      "type": "plain_text",
      "text": "Link"
    },
    "element": {
      "type": "url_text_input",
      "action_id": "url_a"
    }
  },
  {
    "type": "input",
    "block_id": "opt_b",
    "optional": true,
    "label": {
      "type": "plain_text",
      "text": "Optional comment"
    },
    "element": {
      "type": "plain_text_input",
      "action_id": "opt_a"
    }
  },
  {
    "type": "input",
    "block_id": "dispatch_b",
    "dispatch_action": true,
    "label": {
      "type": "plain_text",
      "text": "Search (press Enter)"
    },
    "element": {
      "type": "plain_text_input",
      "action_id": "dispatch_a",
      "dispatch_action_config": {
        "trigger_actions_on": [
          "on_enter_pressed"
        ]
      }
    }
  }
]

Every input block needs a label, and the field goes in element. Slack filled in grey placeholders we did not set: "Write something" for text fields, "Enter a number", "Enter an email address" and "Enter a URL". With max_length: 30 the Title box showed a live counter. The block_id and action_id values come back as the two keys in state.values, so set both yourself instead of letting Slack generate random ones.

What Slack checks before your app sees anything

We clicked Submit with every field empty, then with bad values. Slack showed the errors in the modal and our app received no event at all for either attempt.

What we enteredMessage under the fieldSent to the app?
Required text field left empty"Please complete this required field."No
Email left empty"An email address is required"No
11 in a number field with max_value 10"Number cannot be greater than 10."No
not-an-email in the email field"You must enter a valid email address"No
example in the URL fieldnoneYes, value "example"
Optional field left emptynoneYes, value null
The same modal after Submit with bad values: Quantity shows 11 with Number cannot be greater than 10, and Email shows not-an-email with You must enter a valid email address

The URL field did not check anything. example, with no scheme and no dot, reached our app as-is, so validate URLs on your side. Only fields marked "optional": true may be left empty; every other input block is required by default, and the label shows "(optional)" next to the optional ones.

The view_submission payload

When the values passed Slack's checks, our app got an interactive envelope with type: view_submission. This is view.state.values from that submission, copied from our log:

{
  "title_b": {
    "title_a": {
      "type": "plain_text_input",
      "value": "error"
    }
  },
  "notes_b": {
    "notes_a": {
      "type": "plain_text_input",
      "value": "line one\nline two"
    }
  },
  "qty_b": {
    "qty_a": {
      "type": "number_input",
      "value": "3"
    }
  },
  "email_b": {
    "email_a": {
      "type": "email_text_input",
      "value": "jane@example.com"
    }
  },
  "url_b": {
    "url_a": {
      "type": "url_text_input",
      "value": "example"
    }
  },
  "opt_b": {
    "opt_a": {
      "type": "plain_text_input",
      "value": null
    }
  },
  "dispatch_b": {
    "dispatch_a": {
      "type": "plain_text_input",
      "value": "q4 roadmap"
    }
  }
}

Three things to handle in code. The number arrives as a string ("3"), not a number. The multiline text keeps its line break as \n. An empty optional field is null, not an empty string. The outer key is the block_id and the inner key is the action_id, so read a value as values["qty_b"]["qty_a"]["value"].

Never appear "away" on Slack again

Cloud-based. No downloads. Works 24/7 even when your laptop is off.

Returning errors with response_action

To reject a submission and keep the modal open, answer the view_submission with response_action: "errors" and a map of block_id to message. This is the part of our handler that did it:

def handle(client, req):
    p = req.payload
    if req.type == "interactive" and p["type"] == "view_submission":
        vals = p["view"]["state"]["values"]
        title = vals["title_b"]["title_a"]["value"]
        if title == "error":
            body = {"response_action": "errors",
                    "errors": {"title_b": "Title cannot be 'error'.",
                               "email_b": "We only accept work emails."}}
            client.send_socket_mode_response(
                SocketModeResponse(envelope_id=req.envelope_id, payload=body))
            return
    client.send_socket_mode_response(SocketModeResponse(envelope_id=req.envelope_id))

We typed error as the title and clicked Submit. The modal stayed open and showed our two messages in red under the Title and Email fields, the same style as Slack's own checks. A plain acknowledgement with no body closed the modal. The views.update and views.push guide covers the other response_action values, update, push and clear.

The modal after the app answered with response_action errors: Title cannot be 'error'. under the Title field and We only accept work emails. under the Email field

dispatch_action: get the value on Enter

An input block with "dispatch_action": true sends a block_actions payload when the user presses Enter, without submitting the modal. Slack drew a hint under the field: "Press 'enter' to submit". We typed q4 roadmap and pressed Enter. Our app got this in actions, and the payload also carried the current view.state.values of every field in the modal:

[
  {
    "type": "plain_text_input",
    "block_id": "dispatch_b",
    "action_id": "dispatch_a",
    "value": "q4 roadmap",
    "action_ts": "1791339281.161906"
  }
]
The Search field with q4 roadmap typed and the hint Press enter to submit under it

Input blocks in messages

We posted an input block with chat.postMessage and a bot token, and Slack accepted it (ok: true) and drew a text box in the channel. Slack added dispatch_action_config with on_enter_pressed to the stored block on its own. The difference is what reaches your app:

Block in a messageTyped Ada or Grace, pressed Enter
Input block, dispatch_action false (default)Nothing was sent to the app
Input block, dispatch_action trueblock_actions with the value, state.values and container.type: message
plain_text_input inside an actions blockNot postable: invalid_blocks, "unsupported type: plain_text_input"
A bot message with two input blocks labeled Name: the first holds Ada with no hint, the second holds Grace with Press enter to submit under it

So in a message, set dispatch_action: true, or the user can type into the box and nothing happens. A select menu is the other way to collect a choice in a message; our select menu test covers static_select and external_select. If views.open fails before the modal shows, the trigger_id may have expired; we measured how long it lasts.

Always Active

Stop Jiggling Your Mouse.

Join hundreds of remote workers who never worry about their Slack status. Set it up once, stay green forever.

Related Articles

Guide

Slack API in Postman: A 16-Request Collection We Ran With Newman, and the Errors It Catches

How to call the Slack Web API from Postman with a bearer token and variables, plus a 16-request collection we ran against a real workspace with newman: 16 requests, 17 assertions, 0 failures, and the five mistakes it checks.

Slack Green Team
Guide

Slack Spell Check Not Working: The Setting, and the Grammarly Conflict We Measured

Slack has one spell check switch, under Preferences > Language & region. In our test browser it was on, yet Slack web showed no red underlines: the Grammarly extension had set spellcheck to false on the message box. How to check yours.

Slack Green Team
Guide

Slack Workspace Icon: Size, Crop and Transparency, Tested on a Real Workspace

We uploaded six workspace icons to a test Slack workspace: 100 px, 1024 px, a wide image, a transparent PNG and two oversize files. What Slack accepted, what it stored, and how to remove an icon.

Slack Green Team