Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
171 changes: 111 additions & 60 deletions apps/guide/content/docs/legacy/interactions/modals.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Modals
---

With modals you can create pop-up forms that allow users to provide you with formatted inputs through submissions. We'll cover how to create, show, and receive modal forms using discord.js!
With modals you can create pop-up forms that allow users to provide you with formatted inputs through submissions. We'll cover how to create, show, and receive modals
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
With modals you can create pop-up forms that allow users to provide you with formatted inputs through submissions. We'll cover how to create, show, and receive modals
With modals you can create pop-up forms that allow users to provide you with formatted inputs through submissions. We'll cover how to create, show, and receive modals using discord.js!


<Callout>
This page is a follow-up to the [interactions (slash commands) page](../slash-commands/advanced-creation). Please
Expand All @@ -14,8 +14,8 @@ With modals you can create pop-up forms that allow users to provide you with for
Unlike message components, modals aren't strictly components themselves. They're a callback structure used to respond to interactions.

<Callout>
You can have a maximum of five `ActionRowBuilder`s per modal builder, and one `TextInputBuilder` within an
`ActionRowBuilder`. Currently, you can only use `TextInputBuilder`s in modal action rows builders.
You can have a maximum of five `Label` or `Text Display` components per modal. Similarly a `Label` must only contain
one component.
</Callout>

To create a modal you construct a new `ModalBuilder`. You can then use the setters to add the custom id and title.
Expand All @@ -36,25 +36,15 @@ client.on(Events.InteractionCreate, async (interaction) => {

<Callout>
The custom id is a developer-defined string of up to 100 characters. Use this field to ensure you can uniquely define
all incoming interactions from your modals!
all incoming interactions from your modals.
Comment on lines 38 to +39
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

while we're changing this page, i think we can use much clearer wording here.
i'd propose something more like "[...]. Use this field to uniquely identify modals in incoming interactions" or something along those lines (feel free to adapt wording, if needed, but i hope the point comes across)

</Callout>

The next step is to add the input fields in which users responding can enter free-text. Adding inputs is similar to adding components to messages.
The next step is to add a Modal components to the `modalBuilder`. Which users responding can enter free-text. Adding inputs is similar to adding components to messages.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Modal components" shouldn't be plural.
The second sentence could be rewritten with the new select menus in mind; entering free-text isn't necessarily the case anymore.


At the end, we then call `ChatInputCommandInteraction#showModal` to display the modal to the user.

<Callout type="warn">
If you're using typescript you'll need to specify the type of components your action row holds. This can be done by specifying the generic parameter in `ActionRowBuilder`:

```diff
- new ActionRowBuilder()
+ new ActionRowBuilder<ModalActionRowComponentBuilder>()
```

</Callout>

```js
const { ActionRowBuilder, Events, ModalBuilder, TextInputBuilder, TextInputStyle } = require('discord.js');
const { Events, LabelBuilder, ModalBuilder, TextInputBuilder, TextInputStyle } = require('discord.js');

client.on(Events.InteractionCreate, async (interaction) => {
if (!interaction.isChatInputCommand()) return;
Expand All @@ -63,70 +53,51 @@ client.on(Events.InteractionCreate, async (interaction) => {
// Create the modal
const modal = new ModalBuilder().setCustomId('myModal').setTitle('My Modal');

// Add components to modal

// [!code focus:31]
// Create the text input components
const favoriteColorInput = new TextInputBuilder()
.setCustomId('favoriteColorInput')
// The label is the prompt the user sees for this input
.setLabel("What's your favorite color?")
// Short means only a single line of text
.setStyle(TextInputStyle.Short);

const hobbiesInput = new TextInputBuilder()
.setCustomId('hobbiesInput')
.setLabel("What's some of your favorite hobbies?")
// Paragraph means multiple lines of text.
.setStyle(TextInputStyle.Paragraph);
.setStyle(TextInputStyle.Paragraph)
// Uninteractable text inside of the text input
.setPlaceholder('card games, films, books, etc.');

// An action row only holds one text input,
// so you need one action row per text input.
const firstActionRow = new ActionRowBuilder().addComponents(favoriteColorInput);
const secondActionRow = new ActionRowBuilder().addComponents(hobbiesInput);
// Creating labels for the text input components
const favoriteColorLabel = new LabelBuilder()
// The label is the prompt the user sees for this component
.setLabel("What's your favorite color?")
// Add the text input to the label
.setTextInputComponent(favoriteColorInput);

const hobbiesLabel = new LabelBuilder()
.setLabel("What's some of your favorite hobbies?")
// The description is a small text under the label and above the interactive component
.setDescription('Activities you like to participate in')
.setTextInputComponent(hobbiesInput);

// Add inputs to the modal
modal.addComponents(firstActionRow, secondActionRow);
// Add labels to the modal
modal.addLabelComponents(favoriteColorLabel, hobbiesLabel);

// Show the modal to the user
await interaction.showModal(modal); // [!code word:showModal]
}
});
```

Restart your bot and invoke the `/ping` command again. You should see a popup form resembling the image below:
Restart your bot and invoke the `/ping` command again. You should see the modal as imaged below:

![Modal Example](./images/modal-example.png)

<Callout type="warn">
Showing a modal must be the first response to an interaction. You cannot `defer()` or `deferUpdate()` then show a
Showing a modal must be the first response to an interaction. You cannot `deferReply()` or `deferUpdate()` then show a
modal later.
</Callout>

### Input styles

Currently there are two different input styles available:

- `Short`, a single-line text entry;
- `Paragraph`, a multi-line text entry similar to the HTML `<textarea>`;

### Input properties

In addition to the `customId`, `label` and `style`, a text input can be customised in a number of ways to apply validation, prompt the user, or set default values via the `TextInputBuilder` methods:

```js
const input = new TextInputBuilder()
// set the maximum number of characters to allow
.setMaxLength(1_000)
// set the minimum number of characters required for submission
.setMinLength(10)
// set a placeholder string to prompt the user
.setPlaceholder('Enter some text!')
// set a default value to pre-fill the input
.setValue('Default')
// require a value in this input field
.setRequired(true);
```

## Receiving modal submissions

### Interaction collectors
Expand Down Expand Up @@ -165,14 +136,15 @@ If the modal was shown from a `ButtonInteraction` or `StringSelectMenuInteractio
```js
client.on(Events.InteractionCreate, async (interaction) => {
if (!interaction.isModalSubmit()) return;
// [!code focus:3]
if (interaction.customId === 'myModal') {
await interaction.reply({ content: 'Your submission was received successfully!' });
}
});
```

<Callout>
If you're using typescript, you can use the `ModalSubmitInteraction#isFromMessage` typeguard, to make sure the
If you're using typescript, you can use the `ModalSubmitInteraction#isFromMessage` type guard, to make sure the
received interaction was from a `MessageComponentInteraction`.
</Callout>

Expand All @@ -183,10 +155,89 @@ You'll most likely need to read the data sent by the user in the modal. You can
```js
client.on(Events.InteractionCreate, (interaction) => {
if (!interaction.isModalSubmit()) return;
if (interaction.customId === 'myModal') {
await interaction.reply({ content: 'Your submission was received successfully!' });

// Get the data entered by the user
const favoriteColor = interaction.fields.getTextInputValue('favoriteColorInput');
const hobbies = interaction.fields.getTextInputValue('hobbiesInput');
console.log({ favoriteColor, hobbies });
// [!code focus:5]
// Get the data entered by the user
const favoriteColor = interaction.fields.getTextInputValue('favoriteColorInput');
const hobbies = interaction.fields.getTextInputValue('hobbiesInput');

console.log({ favoriteColor, hobbies });
}
});
```

## Modal Components

Modals are built using components.

<Callout>
Current supported component for modals are: - [Label](#label) - A layout component to add interactive components to
modals - [Text Display](#text-display) - A content component used to contain text
</Callout>
Comment on lines +175 to +178
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this callout is beneficial to the guide and may cause further maintenance work if more components are added.


### Label

A Layout component, labels are used to display a label and description ore interactive components in modals.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
A Layout component, labels are used to display a label and description ore interactive components in modals.
Labels are layout components used to display a label and description on interactive components in modals.


<Callout type="warn">
`label` can be a max length of 45 characters `description` can be a max length of 100 characters
</Callout>
<Callout>
Labels need to have one interactive components. Current supported component for labels are: - [Text
Input](#text-input) - An interactive component allowing free form text input - [Select
Menus](../interactive-components/select-menus#using-select-menus-in-modals) - Interactive components allowing for
limiting user input to users, roles, channels, and preselected options
</Callout>
Comment on lines +187 to +192
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, not a huge fan of sections that need to be remembered if more components are added in the future.


### Text Input

### Input styles
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
### Input styles
#### Input styles

Assuming this should be nested under "Input". H3 (empty) > H3 is not valid


Currently there are two different input styles available:

- `Short`, a single-line text entry
- `Paragraph`, a multi-line text entry

### Input properties
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
### Input properties
#### Input properties

same as above


In addition to the `customId` and `style`, a text input can be customised in a number of ways to apply validation, prompt the user, or set default values via the `TextInputBuilder` methods:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prefer using the american english word for "customised" ("customized")


```js
const input = new TextInputBuilder()
// set the component id (this is not the custom id)
.setId(0)
// Set the maximum number of characters to allowed
.setMaxLength(1_000)
// Set the minimum number of characters required for submission
.setMinLength(10)
// Set a default value to pre-fill the text input
.setValue('Default')
// Require a value in this text input field (defaults to true)
.setRequired(true);
```

### Text Display

Modals support adding a texts display. Unlike interactive components a text display is added to the modal builder, without being put in a label first.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"texts" is not supposed to be plural.
(also, did you forget to say text display component instead of text display?)


<Callout>
Adding text display components decrees the number of labels that can be added to the modal. The modal only has maximum

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did you mean "decreases"?
also use "has a maximum..." (you forgot the "a")

of five `Label` or `Text Display` components.
</Callout>

```js
const modal = new ModalBuilder().setCustomId('myModal').setTitle('My Modal');

// Set the content of the text display
const text = new TextDisplayBuilder().setContent(
"## Important Information\nAccording to all known laws of aviation, there is no way a bee should be able to fly. Its wings are too small to get its fat little body off the ground. The bee, of course, flies anyway because bees don't care what humans think is impossible.",
);

// Add the text display to the Modal
modal.addTextDisplayComponents(text);
```

Below image is an example of a modal with only one text display in it:
![Modal with on text display example](./images/modal-text-display-example.png)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as per Discord devs, display-only modals being supported is more a side effect and a "not explicitly handled to be denied" rather than a fully thought-through feature. (It having 2 buttons makes little sense, for example).

i'd prefer showcasing a modal that shows all the "base" types of components
(should file upload or other more complex/unusual components be added, i can see those having their own spotlight!)

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading