Skip to content
This repository was archived by the owner on Oct 5, 2025. It is now read-only.

Commit 13fd0be

Browse files
committed
updated model page for label and select menu component
1 parent db4d76c commit 13fd0be

File tree

1 file changed

+80
-29
lines changed

1 file changed

+80
-29
lines changed

guide/interactions/modals.md

Lines changed: 80 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ This page is a follow-up to the [interactions (slash commands) page](/slash-comm
1111
Unlike message components, modals aren't strictly components themselves. They're a callback structure used to respond to interactions.
1212

1313
::: tip
14-
You can have a maximum of five <DocsLink path="ActionRowBuilder:Class" />s per modal builder, and one <DocsLink path="TextInputBuilder:Class" /> within an <DocsLink path="ActionRowBuilder:Class" />. Currently, you can only use <DocsLink path="TextInputBuilder:Class" />s in modal action rows builders.
14+
You can have a maximum of five <DocsLink path="LabelBuilder:Class" />s per modal builder, and one <DocsLink path="TextInputBuilder:Class" /> or <DocsLink path="StringSelectMenuBuilder:Class" /> within an <DocsLink path="LabelBuilder:Class" />.
1515
:::
1616

1717
To create a modal you construct a new <DocsLink path="ModalBuilder:Class" />. You can then use the setters to add the custom id and title.
@@ -35,21 +35,12 @@ client.on(Events.InteractionCreate, async interaction => {
3535
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!
3636
:::
3737

38-
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.
38+
The next step is to add the components to which users can respond. Adding components to modals is similar to adding components to messages.
3939

40-
At the end, we then call <DocsLink path="ChatInputCommandInteraction:Class#showModal" type="method"/> to display the modal to the user.
41-
42-
::: warning
43-
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 <DocsLink path="ActionRowBuilder:Class" />
44-
45-
```diff
46-
- new ActionRowBuilder()
47-
+ new ActionRowBuilder<ModalActionRowComponentBuilder>()
48-
```
49-
:::
40+
At the end, we then call <DocsLink path="ChatInputCommandInteraction:Class#showModal" type="method" /> to display the modal to the user.
5041

5142
```js {1,12-34}
52-
const { ActionRowBuilder, Events, ModalBuilder, TextInputBuilder, TextInputStyle } = require('discord.js');
43+
const { Events, ModalBuilder, TextInputBuilder, TextInputStyle, LabelBuilder, StringSelectMenuBuilder } = require('discord.js');
5344

5445
client.on(Events.InteractionCreate, async interaction => {
5546
if (!interaction.isChatInputCommand()) return;
@@ -62,27 +53,58 @@ client.on(Events.InteractionCreate, async interaction => {
6253

6354
// Add components to modal
6455

65-
// Create the text input components
56+
// Create the interactive components
6657
const favoriteColorInput = new TextInputBuilder()
6758
.setCustomId('favoriteColorInput')
68-
// The label is the prompt the user sees for this input
69-
.setLabel("What's your favorite color?")
7059
// Short means only a single line of text
7160
.setStyle(TextInputStyle.Short);
7261

7362
const hobbiesInput = new TextInputBuilder()
7463
.setCustomId('hobbiesInput')
75-
.setLabel("What's some of your favorite hobbies?")
7664
// Paragraph means multiple lines of text.
7765
.setStyle(TextInputStyle.Paragraph);
66+
67+
const favoriteStarterSelect = new StringSelectMenuBuilder()
68+
.setCustomId('starter')
69+
.setPlaceholder('Make a selection!')
70+
.addOptions(
71+
// String select menu options
72+
new StringSelectMenuOptionBuilder()
73+
// Label displayed to user
74+
.setLabel('Bulbasaur')
75+
// Description of option
76+
.setDescription('The dual-type Grass/Poison Seed Pokémon.')
77+
// Value returned to in modal submission
78+
.setValue('bulbasaur'),
79+
new StringSelectMenuOptionBuilder()
80+
.setLabel('Charmander')
81+
.setDescription('The Fire-type Lizard Pokémon.')
82+
.setValue('charmander'),
83+
new StringSelectMenuOptionBuilder()
84+
.setLabel('Squirtle')
85+
.setDescription('The Water-type Tiny Turtle Pokémon.')
86+
.setValue('squirtle'),
87+
);
88+
// An Label component only holds one interactive component,
89+
// so you need one Label component per text input or string select menu.
90+
const favoriteColorLabel = new LabelBuilder()
91+
// The label is the prompt the user sees for this component
92+
.setLabel("What's your favorite color?")
93+
.setTextInputComponent(favoriteColorInput);
7894

79-
// An action row only holds one text input,
80-
// so you need one action row per text input.
81-
const firstActionRow = new ActionRowBuilder().addComponents(favoriteColorInput);
82-
const secondActionRow = new ActionRowBuilder().addComponents(hobbiesInput);
83-
84-
// Add inputs to the modal
85-
modal.addComponents(firstActionRow, secondActionRow);
95+
const hobbiesLabel = new LabelBuilder()
96+
.setLabel("What's some of your favorite hobbies?")
97+
// The Description is small text under the label above the interactive component
98+
.setDescription('card game, film, book, etc.')
99+
.setTextInputComponent(hobbiesInput);
100+
101+
const favoriteStarterLabel = new LabelBuilder()
102+
.setLabel("What's some of your favorite Gen 1 Pokémon starter?")
103+
// The Description is small text under the label above the interactive component
104+
.setStringSelectMenuComponent(favoriteStarterSelect);
105+
106+
// Add Label components to the modal
107+
modal.setLabelComponents(favoriteColorLabel, hobbiesLabel, favoriteStarterLabel);
86108

87109
// Show the modal to the user
88110
await interaction.showModal(modal);
@@ -92,10 +114,11 @@ client.on(Events.InteractionCreate, async interaction => {
92114

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

95-
<img width=450 src="./images/modal-example.png">
117+
<!-- TODO: add new Image -->
118+
<!-- <img width=450 src="./images/modal-example.png"> -->
96119

97120
::: warning
98-
Showing a modal must be the first response to an interaction. You cannot `defer()` or `deferUpdate()` then show a modal later.
121+
Showing a modal must be the first response to an interaction. You cannot `deferReply()` or `deferUpdate()` then show a modal later.
99122
:::
100123

101124
### Input styles
@@ -106,10 +129,12 @@ Currently there are two different input styles available:
106129

107130
### Input properties
108131

109-
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 <DocsLink path="TextInputBuilder:Class" /> methods:
132+
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 <DocsLink path="TextInputBuilder:Class" /> methods:
110133

111134
```js
112135
const input = new TextInputBuilder()
136+
// sets the id (not the custom id) for this component.
137+
.setId(33)
113138
// set the maximum number of characters to allow
114139
.setMaxLength(1_000)
115140
// set the minimum number of characters required for submission
@@ -122,6 +147,31 @@ const input = new TextInputBuilder()
122147
.setRequired(true);
123148
```
124149

150+
### Select Menu properties
151+
152+
In addition to the `customId`, `placeholder` and `options`, a text input can be customised in a number of ways to apply validation, prompt the user the <DocsLink path="StringSelectMenuBuilder:Class" /> methods:
153+
154+
```js
155+
const input = new StringSelectMenuBuilder()
156+
// sets the id (not the custom id) for this component.
157+
.setId(33)
158+
// maximum number of option that could be selected
159+
.setMaxValues(10)
160+
// minimum number of option that could be selected
161+
.setMinValues(2)
162+
// require a selection for this select menu
163+
.setRequired(true);
164+
```
165+
To add set default values of a select menu use `setDefault(true)` in the option builder
166+
167+
```js
168+
const option = StringSelectMenuOptionBuilder()
169+
.setDefault(true)
170+
.setLabel('Charmander')
171+
.setDescription('The Fire-type Lizard Pokémon.')
172+
.setValue('charmander')
173+
```
174+
125175
## Receiving modal submissions
126176

127177
### Interaction collectors
@@ -165,7 +215,7 @@ client.on(Events.InteractionCreate, async interaction => {
165215
```
166216

167217
::: tip
168-
If you're using typescript, you can use the <DocsLink path="ModalSubmitInteraction:Class#isFromMessage" type="method"/> typeguard, to make sure the received interaction was from a `MessageComponentInteraction`.
218+
If you're using typescript, you can use the <DocsLink path="ModalSubmitInteraction:Class#isFromMessage" type="method"/> type guard, to make sure the received interaction was from a <DocsLink path="MessageComponentInteraction:Class"/>.
169219
:::
170220

171221
## Extracting data from modal submissions
@@ -179,6 +229,7 @@ client.on(Events.InteractionCreate, interaction => {
179229
// Get the data entered by the user
180230
const favoriteColor = interaction.fields.getTextInputValue('favoriteColorInput');
181231
const hobbies = interaction.fields.getTextInputValue('hobbiesInput');
182-
console.log({ favoriteColor, hobbies });
232+
const favoriteStarter = interaction.fields.getStringSelectMenuValues('starter')
233+
console.log({ favoriteColor, hobbies, favoriteStarter});
183234
});
184235
```

0 commit comments

Comments
 (0)