Welcome, this post is about the different types of select menus and how to use them. View in discord.py docs

Select #

(technically called a “string select”) This is the most basic select menu, it allows the user to select from a list of (up to 25) custom provided options.

Example:

from typing import Any

import discord
from discord.ui import Select


class FruitSelect(Select):
    def __init__(self):
        super().__init__(
            placeholder="Select a favorite fruit...",
            # the options
            options=[
                # value is what is returned when the option is selected, label is what is shown to the user
                # value is optional, if not provided, label is used as the value
                discord.SelectOption(label="Apple", value="apple"),
                discord.SelectOption(label="Orange", value="orange"),
                discord.SelectOption(label="Banana", value="banana"),
            ]
        )

    async def callback(self, interaction: discord.Interaction) -> Any:
        selected_fruit: str = self.values[0] # either "apple", "orange" or "banana"
        await interaction.response.send_message(
            f"{interaction.user.mention}'s favorite fruit is {selected_fruit}!"
        )

Sending that as a view will result in a select menu that looks like this: string_select

Auto populated selects #

discord.py v2.1+ required

These are select menus that are populated automatically by Discord, they are not really customizable:

  • You can not filter the members, roles or channels shown.
  • You can set the min/max amount of options the user can or must choose using the min_values (1-25) and max_values (1-25) kwargs.
  • You can choose the type of channels that are shown for ChannelSelect.
  • You can specify the options that should be selected by default using the default_values kwarg. (requires discord.py v2.4+) (more on later)
  • Only the first 25 options will be shown, the rest can be searched for using the search bar, which has auto-complete.

UserSelect #

This is a select menu that allows the user to select from a list of members in the server. Or, if used in a DM, you and the bot.

Same as the basic select, except we import and use UserSelect instead of Select:

from discord.ui import UserSelect

And values is a list of discord.Member or discord.User objects:

from typing import Any

async def callback(self, interaction: discord.Interaction) -> Any:
    users: list[discord.Member | discord.User] = self.values
    selected_users = [
        f"Name: {user.name}, ID: {user.id}"
        for user in users
    ]
    await interaction.response.send_message(
        f"{interaction.user.mention} selected the following users:\n" + "\n".join(selected_users)
    )

RoleSelect #

This is a select menu that allows the user to select from a list of roles in the server.

Same as the basic select, except we import and use RoleSelect instead of Select:

from discord.ui import RoleSelect

And values is a list of discord.Role objects:

from typing import Any

async def callback(self, interaction: discord.Interaction) -> Any:
    roles: list[discord.Role] = self.values
    selected_roles = [
        f"Name: {role.name}, ID: {role.id}"
        for role in roles
    ]
    await interaction.response.send_message(
        f"{interaction.user.mention} selected the following roles:\n" + "\n".join(selected_roles)
    )

MentionableSelect #

This is a select menu that allows the user to select from a list of members and roles in the server. Or, if used in a DM, you and the bot.

Same as the basic select, except we import and use MentionableSelect instead of Select:

from discord.ui import MentionableSelect

And values is a list of discord.Member, discord.User and discord.Role objects:

from typing import Any

async def callback(self, interaction: discord.Interaction) -> Any:
    mentionables: list[discord.Member | discord.User | discord.Role] = self.values
    selected_users = [
        f"Name: {user.name}, ID: {user.id}"
        for user in mentionables
        if isinstance(user, (discord.Member, discord.User))
    ]
    selected_roles = [
        f"Name: {role.name}, ID: {role.id}"
        for role in mentionables
        if isinstance(role, discord.Role)
    ]
    await interaction.response.send_message(
        (
            f"{interaction.user.mention} selected the following users:\n" + "\n".join(selected_users)
            f"\nand the following roles:\n" + "\n".join(selected_roles)
        )

    )

ChannelSelect #

This is a select menu that allows the user to select from a list of channels in the server.

Same as the basic select, except we import and use ChannelSelect instead of Select and we can use the channel_types kwarg to limit the type of channels shown, all channels are shown by default:

from discord import ChannelType
from discord.app_commands import AppCommandChannel, AppCommandThread
from discord.ui import ChannelSelect

And values is a list of discord.AppCommandChannel or discord.AppcommandThread objects:

from typing import Any

class ChannelsSelector(ChannelSelect):
    def __init__(self):
        super().__init__(
            placeholder="Select a channel...",
            # limit the type of channels shown to text, voice and threads
            channel_types=[ChannelType.text, ChannelType.voice, ChannelType.thread]

    async def callback(self, interaction: discord.Interaction) -> Any:
        channels: list[AppCommandChannel | AppCommandThread] = self.values
        selected_channels = [
            f"Name: {channel.name}, ID: {channel.id}"
            for channel in channels
        ]
        await interaction.response.send_message(
            f"{interaction.user.mention} selected the following channels:\n" + "\n".join(selected_channels)
        )

default_values #

discord.py v2.4+ required

This was briefly mentioned above, but you can specify the options that should be selected by default using the default_values kwarg.

The kwarg takes a list of objects, the type of which depends on the type of select menu, e.g. for a ChannelSelect it would be a list of channel objects.

There is also special class for it called SelectDefaultValue which takes the id of a user, role of channel and the type of the object or you can also use the from_user, from_role and from_channel classmethods to create the object. This one can be used for all the types of selects.

from typing import Any

import discord
from discord import ui


class ChooseAnyoneExceptMe(ui.View):
    def __init__(self, bot_owner_id: int):
        super().__init__()

        # set the default values of the user select to the bot owner, so they can't select me!
        self.user_select.default_values = [
            discord.SelectDefaultValue(id=bot_owner_id, type=discord.SelectDefaultType.user)
        ]

    @ui.select(cls=ui.UserSelect, placeholder="Select my owner...")
    async def user_select(self, interaction: discord.Interaction, select: ui.UserSelect) -> Any:
        selected_user = select.values[0]
        # yes, we are very sure that the selected user is not the bot owner.
        await interaction.response.send_message(
            f"{selected_user.mention} is not my owner!"
        )

Decorator #

The select menus can also be defined using a decorator in a View subclass and specifying the cls kwarg (defaults to Select):

from typing import Any

from discord import ui


class SelectView(ui.View):
    # string select
    # @ui.select(placeholder="Select an option...". options=[...])
    # or @ui.select(cls=ui.Select, placeholder="Select an option...". options=[...])

    # user select
    # @ui.select(cls=ui.UserSelect, placeholder="Select a user...")
    # role select
    # @ui.select(cls=ui.RoleSelect, placeholder="Select a role...")
    # mentionable select
    # @ui.select(cls=ui.MentionableSelect, placeholder="Select a role or user...")

    # channel select
    # only text, voice and threads are shown
    @ui.select(cls=ui.ChannelSelect, channel_types=[ChannelType.text, ChannelType.voice, ChannelType.thread], placeholder="Select a channel...")
    async def channel_select(self, interaction: discord.Interaction, select: ui.ChannelSelect) -> Any:
        ...