> ## Documentation Index
> Fetch the complete documentation index at: https://docs.erlc.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Buttons, Select Menus, and Modals in erlc.dev Studio

> Build interactive Discord UIs with buttons, select menus, polls, and modal forms. Wire them to event triggers to handle user interactions.

Studio lets you build interactive Discord interfaces that respond to real user input — from clickable buttons and dropdown menus to full pop-up modal forms. This page explains how to create each type of interactive component, attach event triggers, and read the data that users submit.

## Buttons

Buttons appear inside Discord embeds and let users trigger automation flows with a single click.

### Adding a Button to an Embed

Place an **`erlc_embed_button`** block inside any embed's `BLOCKS` statement to add a clickable button.

| Input / Field | Description |
| - | - |
| `LABEL` input | The text displayed on the button |
| `EMOJI` input | Optional emoji shown alongside the label |
| `CUSTOM_ID` field | A unique string that identifies this button — used to route click events |
| `STYLE` field | `1` = Primary (blue), `2` = Secondary (grey), `3` = Success (green), `4` = Danger (red) |
| `DISABLED` field | Checkbox — renders the button in a disabled state |

Use **`erlc_embed_link_button`** to create a button that opens an external URL rather than firing an event. It accepts the same `LABEL`, `EMOJI`, and `DISABLED` inputs, plus a `URL` input for the destination link.

<Note>
  Link buttons do not trigger automation events. Only `erlc_embed_button` (with a `CUSTOM_ID`) fires the `erlc_when_discord_button` trigger.
</Note>

### Handling Button Clicks

Use the **`erlc_when_discord_button`** trigger block to run a flow when a user clicks a specific button. Set the `BUTTON_ID` field to match the `CUSTOM_ID` of the button you want to handle.

Inside the triggered flow, read the click context using **`erlc_discord_event_value`** and selecting the `ButtonId` field to retrieve which button was clicked.

To programmatically disable a button after it is clicked, use **`erlc_bot_disable_clicked_button`**. This block requires no inputs and automatically targets the button that triggered the current event.

<Tip>
  Use `erlc_bot_disable_clicked_button` immediately after handling a one-time action (such as approving a request) to prevent duplicate submissions.
</Tip>

***

## Select Menus

Select menus give users a dropdown of choices to pick from. You can embed them directly in an embed or send them as a standalone message.

### Embed Select Menu

Add a **`erlc_embed_select_menu`** block inside an embed's `BLOCKS` statement and nest **`erlc_embed_select_option`** blocks inside its `OPTIONS` statement.

**`erlc_embed_select_menu`** fields and inputs:

| Input / Field | Description |
| - | - |
| `PLACEHOLDER` input | Hint text shown before the user makes a selection |
| `OPTIONS` statement | Connect `erlc_embed_select_option` blocks here |
| `CUSTOM_ID` field | Unique ID used to identify this menu in select events |
| `MIN_VALUES` field | Minimum number of options the user must select |
| `MAX_VALUES` field | Maximum number of options the user can select |
| `DISABLED` field | Checkbox — disables the menu |

**`erlc_embed_select_option`** inputs:

| Input | Description |
| - | - |
| `LABEL` input | Display text for the option |
| `VALUE` input | Internal value returned when this option is selected |
| `DESCRIPTION` input | Optional short description beneath the label |
| `EMOJI` input | Optional emoji displayed with the option |

### Standalone Select Menu

Use **`erlc_bot_send_select_menu`** to send a select menu as a standalone message in a channel without wrapping it in an embed.

| Input / Field | Description |
| - | - |
| `CHANNEL_ID` input | The channel to send the menu to |
| `CONTENT` input | Optional message text displayed above the menu |
| `CUSTOM_ID` input | Unique ID for the menu |
| `PLACEHOLDER` input | Placeholder text |
| `SELECT_TYPE` field | `5` = Text options, `6` = User picker, `7` = Role picker, `8` = Mentionable picker |

For resource-based menus — where options are populated dynamically from your server's data — use **`erlc_bot_send_resource_select`** instead.

### Handling Select Events

Use the **`erlc_when_discord_select`** trigger and set its `CUSTOM_ID` field to match the menu you want to handle.

Read the user's selection with **`erlc_discord_event_value`**:

| Field | Description |
| - | - |
| `SelectedValue` | The single value the user chose (when `MAX_VALUES` is 1) |
| `SelectedValues` | A list of all selected values (when multiple selections are allowed) |

***

## Polls

Use **`erlc_bot_send_poll`** to send a native Discord poll to a channel.

| Input / Field | Description |
| - | - |
| `CHANNEL_ID` input | The channel to send the poll to |
| `QUESTION` input | The poll question text |
| `ANSWERS` input | The list of answer options |
| `HOURS` input | How long the poll remains open (in hours) |
| `MULTIPLE` field | `true` to allow multiple votes, `false` for single choice only |

***

## Modals

Modals are pop-up forms that appear inside Discord when triggered by a button click or slash command interaction. They can collect text input, checkboxes, radio selections, file uploads, and more.

### Opening a Modal

Use **`erlc_modal_open`** to display a modal to the user. This block must run inside an interaction context (such as a button click handler).

| Input | Description |
| - | - |
| `CUSTOM_ID` input | Unique ID for this modal — used to route submit events |
| `TITLE` input | The title displayed at the top of the modal |
| `FIELDS` statement | Connect modal field blocks here |

### Modal Field Blocks

Place these blocks inside the `FIELDS` statement of `erlc_modal_open` to define the form's inputs.

<Accordion title="Text Input">
  **`erlc_modal_text_input`** — A single-line or multi-line text field.

  | Field | Description |
  | - | - |
  | `LABEL` | Field label shown to the user |
  | `CUSTOM_ID` | Unique field ID used to retrieve the submitted value |
  | `DESCRIPTION` | Optional helper text below the label |
  | `REQUIRED` | Whether the field must be filled in |
  | `STYLE` | `1` = short (single line), `2` = paragraph (multi-line) |
  | `PLACEHOLDER` | Placeholder text inside the input |
  | `MIN_LENGTH` | Minimum character count |
  | `MAX_LENGTH` | Maximum character count |
</Accordion>

<Accordion title="Checkbox">
  **`erlc_modal_checkbox`** — A single on/off checkbox.

  | Field | Description |
  | - | - |
  | `LABEL` | Checkbox label |
  | `CUSTOM_ID` | Unique field ID |
  | `DESCRIPTION` | Optional helper text |
  | `REQUIRED` | Whether the user must interact with this field |
  | `DEFAULT` | The default checked state |
</Accordion>

<Accordion title="Checkbox Group">
  **`erlc_modal_checkbox_group`** — A group of checkboxes where users can select multiple options.

  | Input / Field | Description |
  | - | - |
  | `OPTIONS` statement | Connect `erlc_modal_option` blocks |
  | `LABEL` field | Group label |
  | `CUSTOM_ID` field | Unique group ID |
  | `MIN_VALUES` field | Minimum selections required |
  | `MAX_VALUES` field | Maximum selections allowed |
</Accordion>

<Accordion title="Radio Group">
  **`erlc_modal_radio_group`** — A group of options where the user picks exactly one.

  | Input / Field | Description |
  | - | - |
  | `OPTIONS` statement | Connect `erlc_modal_option` blocks |
  | `LABEL` field | Group label |
  | `CUSTOM_ID` field | Unique group ID |
</Accordion>

<Accordion title="Select Dropdown">
  **`erlc_modal_select`** — A dropdown select menu inside the modal.

  | Input / Field | Description |
  | - | - |
  | `OPTIONS` statement | Connect `erlc_modal_option` blocks |
  | `LABEL` field | Dropdown label |
  | `CUSTOM_ID` field | Unique dropdown ID |
  | `PLACEHOLDER` field | Placeholder text |
  | `MIN_VALUES` / `MAX_VALUES` fields | Selection limits |
</Accordion>

<Accordion title="File Upload">
  **`erlc_modal_file_upload`** — Lets the user attach one or more files.

  | Field | Description |
  | - | - |
  | `LABEL` | Field label |
  | `CUSTOM_ID` | Unique field ID |
  | `REQUIRED` | Whether a file must be provided |
  | `MIN_VALUES` | Minimum number of files |
  | `MAX_VALUES` | Maximum number of files |
</Accordion>

<Accordion title="Option Block">
  **`erlc_modal_option`** — Defines a single selectable option for radio groups, checkbox groups, and select menus.

  | Field | Description |
  | - | - |
  | `LABEL` | Display text |
  | `VALUE` | Internal value returned on submission |
  | `DESCRIPTION` | Optional description |
  | `DEFAULT` | Whether this option is pre-selected |
</Accordion>

### Handling Modal Submissions

Use the **`erlc_when_discord_modal`** trigger and set its `CUSTOM_ID` field to match the `CUSTOM_ID` you provided to `erlc_modal_open`.

Read submitted values using **`erlc_discord_event_value`** with one of the following fields:

| Field | Description |
| - | - |
| `ModalId` | The ID of the submitted modal |
| `ModalFieldId` | The ID of the specific field being read |
| `ModalValue` | The submitted value for a single field |
| `ModalValues` | A list of submitted values (for multi-select fields) |
| `ModalFileId` | Internal ID of an uploaded file |
| `ModalFileName` | Original filename of an uploaded file |
| `ModalFileUrl` | CDN URL to access the uploaded file |
| `ModalFileSize` | File size in bytes |
| `ModalFileContentType` | MIME type of the uploaded file |

***

## Examples

### Embed with a Button + Click Handler

The JSON below sends an embed with a "Confirm" button, then shows how the `erlc_when_discord_button` trigger listens for it.

```json theme={null}
{
  "type": "erlc_embed_send",
  "id": "send_embed_btn",
  "fields": { "EMBED_VERSION": "v2" },
  "inputs": {
    "CHANNEL_ID": {
      "block": {
        "type": "erlc_discord_channel_value",
        "id": "ch_val",
        "fields": { "ID": "1234567890123456789" }
      }
    },
    "BLOCKS": {
      "block": {
        "type": "erlc_embed_description",
        "id": "desc_btn",
        "inputs": {
          "VALUE": {
            "block": {
              "type": "text",
              "id": "desc_text_btn",
              "fields": { "TEXT": "Click below to confirm your on-duty status." }
            }
          }
        },
        "next": {
          "block": {
            "type": "erlc_embed_button",
            "id": "confirm_btn",
            "fields": {
              "CUSTOM_ID": "confirm_duty",
              "STYLE": "3",
              "DISABLED": "false"
            },
            "inputs": {
              "LABEL": {
                "block": {
                  "type": "text",
                  "id": "btn_label",
                  "fields": { "TEXT": "Go On Duty" }
                }
              }
            }
          }
        }
      }
    }
  }
}
```

The corresponding button trigger and handler:

```json theme={null}
{
  "type": "erlc_when_discord_button",
  "id": "btn_trigger",
  "fields": { "BUTTON_ID": "confirm_duty" },
  "next": {
    "block": {
      "type": "erlc_bot_disable_clicked_button",
      "id": "disable_btn",
      "next": {
        "block": {
          "type": "erlc_embed_send_hidden",
          "id": "hidden_reply",
          "inputs": {
            "BLOCKS": {
              "block": {
                "type": "erlc_embed_description",
                "id": "hidden_desc",
                "inputs": {
                  "VALUE": {
                    "block": {
                      "type": "text",
                      "id": "hidden_text",
                      "fields": { "TEXT": "You are now marked as on duty!" }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
```

***

### Modal with a Text Input Field

The JSON below opens a modal with a single short-text field asking for a reason.

```json theme={null}
{
  "type": "erlc_modal_open",
  "id": "open_modal_1",
  "inputs": {
    "CUSTOM_ID": {
      "block": {
        "type": "text",
        "id": "modal_id_val",
        "fields": { "TEXT": "report_form" }
      }
    },
    "TITLE": {
      "block": {
        "type": "text",
        "id": "modal_title_val",
        "fields": { "TEXT": "Submit a Report" }
      }
    },
    "FIELDS": {
      "block": {
        "type": "erlc_modal_text_input",
        "id": "reason_field",
        "fields": {
          "LABEL": "Reason",
          "CUSTOM_ID": "report_reason",
          "DESCRIPTION": "Briefly describe the incident.",
          "REQUIRED": "true",
          "STYLE": "2",
          "PLACEHOLDER": "Enter your reason here...",
          "MIN_LENGTH": "10",
          "MAX_LENGTH": "500"
        }
      }
    }
  }
}
```

The modal submit trigger:

```json theme={null}
{
  "type": "erlc_when_discord_modal",
  "id": "modal_trigger",
  "fields": { "CUSTOM_ID": "report_form" }
}
```

<Info>
  Use `erlc_discord_event_value` with the `ModalValue` field inside your `erlc_when_discord_modal` flow to read what the user typed into `report_reason`.
</Info>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.