Select menus provide dropdown interfaces for users to choose from predefined options or select Discord entities like users, roles, and channels.
Abstract base class for all select menu builders with common functionality.
abstract class BaseSelectMenuBuilder {
readonly data: Partial<APISelectMenuComponent>;
constructor(data?: Partial<APISelectMenuComponent>);
setCustomId(customId: string): this;
setPlaceholder(placeholder: string): this;
setMinValues(minValues: number): this;
setMaxValues(maxValues: number): this;
setDisabled(disabled?: boolean): this;
abstract toJSON(): APISelectMenuComponent;
}Select menu with custom string options defined by the developer.
class StringSelectMenuBuilder extends BaseSelectMenuBuilder {
readonly options: StringSelectMenuOptionBuilder[];
constructor(data?: APIStringSelectComponent);
addOptions(...options: RestOrArray<StringSelectMenuOptionBuilder | APISelectMenuOption>): this;
setOptions(...options: RestOrArray<StringSelectMenuOptionBuilder | APISelectMenuOption>): this;
spliceOptions(index: number, deleteCount: number, ...options: RestOrArray<StringSelectMenuOptionBuilder | APISelectMenuOption>): this;
toJSON(): APIStringSelectComponent;
}Individual option for string select menus.
class StringSelectMenuOptionBuilder {
readonly data: Partial<APISelectMenuOption>;
constructor(data?: APISelectMenuOption);
setLabel(label: string): this;
setValue(value: string): this;
setDescription(description: string): this;
setDefault(isDefault?: boolean): this;
setEmoji(emoji: APIMessageComponentEmoji): this;
toJSON(): APISelectMenuOption;
}Select menu for choosing Discord users.
class UserSelectMenuBuilder extends BaseSelectMenuBuilder {
readonly default_users?: Snowflake[];
constructor(data?: APIUserSelectComponent);
setDefaultUsers(...users: Snowflake[]): this;
toJSON(): APIUserSelectComponent;
}Select menu for choosing Discord roles.
class RoleSelectMenuBuilder extends BaseSelectMenuBuilder {
readonly default_roles?: Snowflake[];
constructor(data?: APIRoleSelectComponent);
setDefaultRoles(...roles: Snowflake[]): this;
toJSON(): APIRoleSelectComponent;
}Select menu for choosing mentionable entities (users or roles).
class MentionableSelectMenuBuilder extends BaseSelectMenuBuilder {
readonly default_users?: Snowflake[];
readonly default_roles?: Snowflake[];
constructor(data?: APIMentionableSelectComponent);
setDefaultUsers(...users: Snowflake[]): this;
setDefaultRoles(...roles: Snowflake[]): this;
toJSON(): APIMentionableSelectComponent;
}Select menu for choosing Discord channels with optional channel type filtering.
class ChannelSelectMenuBuilder extends BaseSelectMenuBuilder {
readonly default_channels?: Snowflake[];
readonly channel_types?: ChannelType[];
constructor(data?: APIChannelSelectComponent);
addChannelTypes(...channelTypes: ChannelType[]): this;
setChannelTypes(...channelTypes: ChannelType[]): this;
setDefaultChannels(...channels: Snowflake[]): this;
toJSON(): APIChannelSelectComponent;
}interface APISelectMenuOption {
label: string;
value: string;
description?: string;
emoji?: APIMessageComponentEmoji;
default?: boolean;
}enum ChannelType {
GuildText = 0,
DM = 1,
GuildVoice = 2,
GroupDM = 3,
GuildCategory = 4,
GuildAnnouncement = 5,
AnnouncementThread = 10,
PublicThread = 11,
PrivateThread = 12,
GuildStageVoice = 13,
GuildDirectory = 14,
GuildForum = 15,
GuildMedia = 16
}type Snowflake = string;import { StringSelectMenuBuilder, StringSelectMenuOptionBuilder, ActionRowBuilder } from "@discordjs/builders";
const selectMenu = new StringSelectMenuBuilder()
.setCustomId('color_select')
.setPlaceholder('Choose a color')
.setMinValues(1)
.setMaxValues(1)
.addOptions(
new StringSelectMenuOptionBuilder()
.setLabel('Red')
.setValue('red')
.setDescription('The color of fire')
.setEmoji({ name: 'π΄', id: null }),
new StringSelectMenuOptionBuilder()
.setLabel('Blue')
.setValue('blue')
.setDescription('The color of water')
.setEmoji({ name: 'π΅', id: null }),
new StringSelectMenuOptionBuilder()
.setLabel('Green')
.setValue('green')
.setDescription('The color of nature')
.setEmoji({ name: 'π’', id: null })
.setDefault(true) // This option will be selected by default
);
const row = new ActionRowBuilder<StringSelectMenuBuilder>()
.addComponents(selectMenu);const multiSelectMenu = new StringSelectMenuBuilder()
.setCustomId('skills_select')
.setPlaceholder('Select your skills (up to 3)')
.setMinValues(1)
.setMaxValues(3)
.addOptions([
{
label: 'JavaScript',
value: 'javascript',
description: 'Web development language'
},
{
label: 'Python',
value: 'python',
description: 'Data science and backend'
},
{
label: 'TypeScript',
value: 'typescript',
description: 'Type-safe JavaScript'
},
{
label: 'Java',
value: 'java',
description: 'Enterprise development'
}
]);import { UserSelectMenuBuilder } from "@discordjs/builders";
const userSelect = new UserSelectMenuBuilder()
.setCustomId('moderator_select')
.setPlaceholder('Select moderators')
.setMinValues(1)
.setMaxValues(5)
.setDefaultUsers('123456789012345678', '987654321098765432');import { RoleSelectMenuBuilder } from "@discordjs/builders";
const roleSelect = new RoleSelectMenuBuilder()
.setCustomId('role_select')
.setPlaceholder('Choose roles to assign')
.setMinValues(0)
.setMaxValues(3)
.setDefaultRoles('555666777888999000');import { ChannelSelectMenuBuilder, ChannelType } from "@discordjs/builders";
const channelSelect = new ChannelSelectMenuBuilder()
.setCustomId('channel_select')
.setPlaceholder('Select a text channel')
.setMinValues(1)
.setMaxValues(1)
.addChannelTypes(ChannelType.GuildText, ChannelType.GuildAnnouncement)
.setDefaultChannels('111222333444555666');import { MentionableSelectMenuBuilder } from "@discordjs/builders";
const mentionableSelect = new MentionableSelectMenuBuilder()
.setCustomId('mention_select')
.setPlaceholder('Select users or roles')
.setMinValues(1)
.setMaxValues(10)
.setDefaultUsers('123456789012345678')
.setDefaultRoles('987654321098765432');const dynamicMenu = new StringSelectMenuBuilder()
.setCustomId('dynamic_menu')
.setPlaceholder('Select an option');
// Add options from an array
const gameOptions = [
{ name: 'Chess', value: 'chess', description: 'Strategic board game' },
{ name: 'Checkers', value: 'checkers', description: 'Classic board game' },
{ name: 'Poker', value: 'poker', description: 'Card game' }
];
const optionBuilders = gameOptions.map(game =>
new StringSelectMenuOptionBuilder()
.setLabel(game.name)
.setValue(game.value)
.setDescription(game.description)
);
dynamicMenu.addOptions(...optionBuilders);
// Replace an option
dynamicMenu.spliceOptions(1, 1,
new StringSelectMenuOptionBuilder()
.setLabel('Backgammon')
.setValue('backgammon')
.setDescription('Ancient board game')
);
// Set all options at once
dynamicMenu.setOptions(
new StringSelectMenuOptionBuilder()
.setLabel('New Game')
.setValue('new_game')
.setDescription('A completely new game')
);const disabledMenu = new StringSelectMenuBuilder()
.setCustomId('disabled_menu')
.setPlaceholder('This menu is disabled')
.setDisabled(true)
.addOptions(
new StringSelectMenuOptionBuilder()
.setLabel('Option 1')
.setValue('option1')
);These aliases are deprecated and will be removed in the next major version:
// Deprecated - use StringSelectMenuBuilder instead
const SelectMenuBuilder = StringSelectMenuBuilder;
// Deprecated - use StringSelectMenuOptionBuilder instead
const SelectMenuOptionBuilder = StringSelectMenuOptionBuilder;Discord enforces these limits on select menus: