Validation
The checks Slack's client runs on a modal before view_submission fires, and how to trigger or override them.
Before a modal’s view_submission ever reaches your app, Slack’s own client checks every input
block: is it filled in when required, is the text within length limits, is the number in range, is the
email or URL well-formed. @nkootstra/block-kit runs the identical checks client-side, so onSubmit
only ever sees a submission that would have passed Slack’s client too.
Try submitting the modal above without an email: the error appears under the field, exactly like
Slack’s own client, and onSubmit never fires.
What gets checked
required?any input element
An input block without optional: true must have a non-empty value.
any input elementmin_length / max_length?plain_text_input
Character count bounds.
plain_text_inputemail format?email_text_input
Basic something@something shape, no spaces.
email_text_inputurl format?url_text_input
Must parse as an http:// or https:// URL.
url_text_inputnumber format?number_input
Must parse as a number; a whole number unless is_decimal_allowed is set.
number_inputmin_value / max_value?number_input
Numeric bounds.
number_inputAn empty rich_text_input (a rich_text value whose sections hold no actual text: no words, only an
empty paragraph) counts as empty for the required check, the same way Slack treats it.
Messages
Every message comes from VALIDATION_MESSAGES, exported so you can read (or override, in your own
copy) Slack’s exact client-side wording:
import { VALIDATION_MESSAGES } from "@nkootstra/block-kit";
VALIDATION_MESSAGES.required;
// "Please complete this required field."
VALIDATION_MESSAGES.minLength(3);
// "Please enter at least 3 characters."
VALIDATION_MESSAGES.maxValue("100");
// "Please enter a number less than or equal to 100."
required?string
stringminLength?(n: number) => string
(n: number) => stringmaxLength?(n: number) => string
(n: number) => stringnumber?string
stringwholeNumber?string
stringminValue?(min: string) => string
(min: string) => stringmaxValue?(max: string) => string
(max: string) => stringemail?string
stringurl?string
stringSlack doesn’t publish the exact wording of these client-side checks; they follow its client’s phrasing as closely as observed.
validateView
<Modal> runs this automatically on Submit, but it’s exported for anywhere you want the same check:
a custom submit button, a test, or validating a view server-side before you even open it:
import { validateView } from "@nkootstra/block-kit";
const errors = validateView(view.blocks, state);
// { email: "Please complete this required field." }
if (Object.keys(errors).length === 0) {
// safe to submit
}
validateView(blocks, state) returns errors keyed by block_id, the same shape as
response_action: "errors" (see Modals), empty when every input block passes.
The errors prop
Errors an app returns from onSubmit (response_action: "errors") apply the way Slack’s client shows
them: under the named block, clearing automatically once that field’s value changes. To seed a modal
(or any surface) with errors up front instead, for a standalone <Modal> you’re previewing, or
errors known before the user even touches the form, pass errors to <BlockKitProvider>:
<BlockKitProvider errors={{ email: "This address is already invited." }}>
<Modal view={view} />
</BlockKitProvider>
<Modal>’s own client-side checks (required/length/format) and any onSubmit errors layer on top of
the provider’s errors rather than replacing them.