Modals

Modal form components for collecting user input.

Modals display form dialogs that collect structured user input. Currently supported on Slack and Teams.

import {
  Modal,
  TextInput,
  DateInput,
  NumberInput,
  Select,
  RadioSelect,
  SelectOption,
} from "chat";

Top-level container for a form dialog. Open a modal from an onAction or onSlashCommand handler using event.openModal().

bot.onAction("open-form", async (event) => {
  await event.openModal(
    Modal({
      callbackId: "feedback",
      title: "Submit Feedback",
      submitLabel: "Send",
      children: [
        TextInput({ id: "comment", label: "Comment", multiline: true }),
      ],
    })
  );
});

Prop

Type

TextInput

A text input field.

TextInput({
  id: "name",
  label: "Your name",
  placeholder: "Enter your name",
})

TextInput({
  id: "description",
  label: "Description",
  multiline: true,
  maxLength: 500,
  optional: true,
})

Prop

Type

DateInput

A date picker — a Slack datepicker, an Adaptive Card Input.Date on Teams.

DateInput({
  id: "due_date",
  label: "Due date",
  placeholder: "Pick a date",
  initialValue: "2026-08-01",
})

Prop

Type

The submitted value arrives in event.values as an ISO YYYY-MM-DD string. An initialValue that is not a valid YYYY-MM-DD date is ignored with a warning — Slack rejects a malformed initial_date by failing the whole modal, so it is dropped rather than forwarded.

NumberInput

A numeric input — a Slack number_input, an Adaptive Card Input.Number on Teams.

NumberInput({
  id: "quantity",
  label: "Quantity",
  min: 1,
  max: 10,
})

Prop

Type

Values in event.values are always strings — parse with Number(...) when you need a number.

Select

Dropdown menu.

Select({
  id: "priority",
  label: "Priority",
  placeholder: "Select priority",
  options: [
    SelectOption({ label: "High", value: "high", description: "Urgent tasks" }),
    SelectOption({ label: "Medium", value: "medium" }),
    SelectOption({ label: "Low", value: "low" }),
  ],
})

Prop

Type

ExternalSelect

Dropdown that loads options dynamically from a handler as the user types. Slack-only. Pair with bot.onOptionsLoad to supply options. See Modals → ExternalSelect for a full example, grouped-options support, and Slack setup notes.

ExternalSelect({
  id: "assignee",
  label: "Assignee",
  placeholder: "Search people",
  minQueryLength: 1,
  initialOption: { label: "Alice", value: "U123" },
})

Prop

Type

The loader registered via bot.onOptionsLoad("assignee", handler) returns either a flat SelectOptionElement[] or OptionsLoadGroup[] ({ label, options }[]) for grouped options.

RadioSelect

Radio button group for mutually exclusive choices.

RadioSelect({
  id: "status",
  label: "Status",
  options: [
    SelectOption({ label: "Open", value: "open" }),
    SelectOption({ label: "Closed", value: "closed" }),
  ],
})

Same props as Select (except placeholder).

SelectOption

An option used inside Select and RadioSelect.

SelectOption({ label: "High", value: "high", description: "Urgent tasks" })

Prop

Type

ModalChild types

The children array in Modal accepts these element types:

TypeCreated by
TextInputElementTextInput()
DateInputElementDateInput()
NumberInputElementNumberInput()
SelectElementSelect()
RadioSelectElementRadioSelect()
TextElementText() — static text content
FieldsElementFields() — key-value display

Read more