Show Modal
Instead of creating a message response you can also show a modal to the user to ask for further information. Modals can have a number of inputs which you can then access using the input(...) variables once the modal has been submitted.
Components
A modal can hold up to 5 components, and at least one of them has to be an input.
- Input: a label, an optional description below it, and one input. Every input has an identifier that you read its answer with, e.g.
{{input('color')}}. - Text: markdown text shown between the inputs, e.g. to explain the form.
These inputs are available:
| Input | Answer |
|---|---|
| Text Input | The entered text. Can be a single line or a paragraph. |
| Select Menu | The values of the picked options. |
| User Select | The IDs of the picked members. |
| Role Select | The IDs of the picked roles. |
| Mentionable Select | The IDs of the picked members or roles. |
| Channel Select | The IDs of the picked channels. You can limit it to channel types. |
| Radio Group | The value of the picked option. Needs 2 to 10 options. |
| Checkbox Group | The values of the picked options. Needs 1 to 10 options. |
| Checkbox | true if it was checked, otherwise false. |
| File Upload | The IDs of the uploaded files. Up to 10 files. |
An option without a value returns its label. Select menus let one option be picked unless you raise Max Picks, and checkbox groups allow all of their options. When several are picked, input(...) returns the first one, like interaction.value of a select menu in a message. inputs(...) returns all of them as a list:
| Example | Result if red and blue were picked |
|---|---|
{{'red' in inputs('colors')}} | true |
{{len(inputs('colors'))}} | 2 |
{{join(inputs('colors'), ', ')}} | red, blue |
inputs(...) works for every input. For a text input, radio group or checkbox it returns a list with the one value.
Inputs that aren't required can be left empty, in which case input(...) returns an empty text and inputs(...) an empty list.
For user, role, mentionable and channel selects, picked(...) returns the picked member, role or channel instead of its ID, and pickedAll(...) all of them as a list. Their fields work like those of user, a role or channel:
| Example | Result |
|---|---|
{{picked('reported').display_name}} | The display name of the picked member |
{{picked('reported')}} | A mention of the picked member |
{{join(map(pickedAll('roles'), #.name), ', ')}} | The names of the picked roles |
File Uploads
A file upload lets the user upload files, like images or screenshots, when submitting the modal. It allows one file unless you raise Max Files, up to 10. input(...) returns the ID of the first file, and picked(...) the file itself, with its url, proxy_url, filename, content_type and size in bytes. On its own it shows as its URL. pickedAll(...) returns all files as a list:
| Example | Result |
|---|---|
{{picked('image')}} | https://cdn.discordapp.com/ephemeral-attachments/... |
{{picked('image').content_type}} | image/png |
{{len(pickedAll('screenshots'))}} | 3 |
{{join(map(pickedAll('screenshots'), #.url), '\n')}} | The URL of every file, one per line |
You can put the URL in an embed's image to show an uploaded image, or in a message to share the file. Discord doesn't limit which kinds of files can be uploaded, so check content_type if you need an image.
Uploaded files are temporary. Discord deletes them after a while, so the URLs stop working. Use them in the flow right after the modal is submitted instead of storing them in a variable for later.
Responding with a modal starts a sub-flow which is suspended until the user submits the modal. See Sub-Flows for more information on how modals work.
The answers stay available as input(...), inputs(...), picked(...) and pickedAll(...) even after later buttons or modals. Command arguments keep working with arg(...) below the modal too.