Skip to main content

Buttons

On May 26, 2021, Discord added a new interaction called buttons. Instead of reactions, bots could now send buttons and users could use them to interact with bots. This opened up a whole new world of possibilities for bots. Soon after, developers made calculators, polls, and games like blackjack, UNO, and even Minecraft! Buttons provided a clear and easy to use interface for interacting with bots.

So, let's learn how you can add buttons to your bot!

Concept​

Buttons weren't the only update to the interactions system in Discord. Discord also added Select Menus and Modal Dialogs, both of which work very similarly to buttons.

These UI elements reside in a "view". To learn more about views, please refer to the interactions page.

Usage Syntax​

Let's see how to create a simple responsive button.

import discord

bot = discord.Bot() # Create a bot object

class View(discord.ui.View): # Create a class called View that subclasses discord.ui.View
@discord.ui.button(label="Click me!", style=discord.ButtonStyle.primary, emoji="😎") # Create a button with the label "😎 Click me!" with color Blurple
async def button_callback(self, button, interaction):
await interaction.response.send_message("You clicked the button!") # Send a message when the button is clicked

@bot.slash_command() # Create a slash command
async def button(ctx):
await ctx.respond("This is a button!", view=View()) # Send a message with our View class that contains the button

bot.run("TOKEN") # Run the bot

Using this command should return the following message:

BobDotComused /button
RobocordBot06/27/2022
This is a button!

As you can see, we create a class called View that subclasses discord.ui.View.

Then, we add a function called button_callback to the View class with the decorator discord.ui.button. This decorator adds a button to a component. This function takes two arguments: the button that was clicked and the interaction. These arguments are passed to the function when the button is clicked by the module. We use the interaction.response.send_message function to send a message to the channel where the interaction was sent.

Finally, we create a global slash command called button that sends the message, along with the view that contains a button.

This is the basic syntax of creating a button. What you create with it is up to you. You can worry about making your button do amazing things, while Pycord handles the rest!

Button Styles​

NameUsageColor
Primarydiscord.ButtonStyle.primary / discord.ButtonStyle.blurpleBlurple
Secondarydiscord.ButtonStyle.secondary / discord.ButtonStyle.grey / discord.ButtonStyle.grayGrey
Successdiscord.ButtonStyle.success / discord.ButtonStyle.greenGreen
Dangerdiscord.ButtonStyle.danger / discord.ButtonStyle.redRed
Linkdiscord.ButtonStyle.link / discord.ButtonStyle.urlGrey

Check out the discord.ButtonStyle class for more information.

Different Button Styles

You can set a button's style by adding the style argument in the discord.ui.button decorator.

class View(discord.ui.View):
@discord.ui.button(label="Click me!", style=discord.ButtonStyle.success)
async def button_callback(self, button, interaction):
await interaction.response.send_message("You clicked the button!")

Action Rows​

We have discussed that Views can have 5 rows. Each row has 5 slots, and each button takes up 1 slot. So, how do we move the buttons to another row?

This can be done by specifying the row argument in the discord.ui.button decorator.

The row argument

The row argument specifies the relative row this button belongs to. A Discord component can only have 5 rows. By default, items are arranged automatically into those 5 rows. If you’d like to control the relative positioning of the row then passing an index is advised. For example, row=1 will show up before row=2. Defaults to None, which is automatic ordering. The row number must be between 0 and 4 (i.e. zero indexed).

class MyView(discord.ui.View):
@discord.ui.button(label="Button 1", row=0, style=discord.ButtonStyle.primary)
async def first_button_callback(self, button, interaction):
await interaction.response.send_message("You pressed me!")

@discord.ui.button(label="Button 2", row=1, style=discord.ButtonStyle.primary)
async def second_button_callback(self, button, interaction):
await interaction.response.send_message("You pressed me!")

Disabling Buttons​

Pre-Disabled Buttons​

class MyView(discord.ui.View):
@discord.ui.button(label="A button", style=discord.ButtonStyle.primary, disabled=True) # pass `disabled=True` to make the button pre-disabled
async def button_callback(self, button, interaction):
...

@bot.command()
async def button(ctx):
await ctx.send("Press the button!", view=MyView())

Disabling Buttons on Press​

class MyView(discord.ui.View):
@discord.ui.button(label="A button", style=discord.ButtonStyle.primary)
async def button_callback(self, button, interaction):
button.disabled = True # set button.disabled to True to disable the button
button.label = "No more pressing!" # change the button's label to something else
await interaction.response.edit_message(view=self) # edit the message's view

Timeouts​

Sometimes, you want to have a button that is disabled after a certain amount of time. This is where timeouts come in.

class MyView(discord.ui.View):
async def on_timeout(self):
for child in self.children:
child.disabled = True
await self.message.edit(content="You took too long! Disabled all the components.", view=self)

@discord.ui.button()
async def button_callback(self, button, interaction):
...

@bot.command()
async def button(ctx):
await ctx.send("Press the button!", view=MyView(timeout=30))

Here, we loop through all the children of the view (buttons and select menus in the view) and disable them. Then, we edit the message to show that the timeout was reached.

note

If the on_timeout coroutine is not present, the components will simply stop working after the specified time.

Persistent Views​

Sometimes, instead of a button that is disabled after a certain amount of time, you want to have a button that is always working.

Normally, when the bot goes offline, all of its buttons stop working. You will be able to see the buttons, but nothing will happen when you press them. This is a problem if you are trying to create a self-role system with buttons, for example. This is where persistent views come in.

Persistent views work forever. When the bot goes offline, the buttons will stop working. When the bot comes back online, however, the buttons will start working again.

In a Persistent View, the timeout must be set to None and all the children in the view much have a custom_id attribute set.

@bot.event
async def on_ready():
bot.add_view(MyView()) # Registers a View for persistent listening

class MyView(discord.ui.View):
def __init__(self):
super().__init__(timeout=None) # timeout of the view must be set to None

@discord.ui.button(label="A button", custom_id="button-1", style=discord.ButtonStyle.primary, emoji="😎") # the button has a custom_id set
async def button_callback(self, button, interaction):
await interaction.response.send_message("Button was pressed", ephemeral=True)

@bot.command()
async def button(ctx):
await ctx.send(f"Press the button! View persistence status: {MyView.is_persistent(MyView())}", view=MyView())

FAQ​

How many buttons can I have in a message?​

Each message can have a maximum of 25 buttons. Views can have up to 5 rows, and each row has 5 slots. A button takes up one slot, while a select menu takes up all five slots.

Can I add more than one view to a message?​

No. As a Discord limitation, you can only have one view per message.

Why are UI Components so confusing?​

They cannot be simple like commands. This system makes them flexible and doesn't limit your imagination. There are loads of different ways you can use UI Components. For example, you could subclass Buttons or Select Menus and add them to a view using the view's add_item function.

UI Components aren't hard to use if you know Python. We recommend learning Object Oriented Programming with Python.

What is OOP? What is subclassing?​

OOP (object-oriented programming) is a programming paradigm that allows you to create objects that have their own properties and methods. Almost everything in python is an object or a class. discord.Embed and discord.ui.View are both classes. When you use view = discord.ui.View() to create a view, you are actually creating an object of type discord.ui.View.

Subclassing is a Python OOP concept. It means that you can create a class that inherits from another class. In other words, the class that subclasses another class can inherit all the methods and attributes of that class.

We highly recommend you learn about basic Python concepts like classes and inheritance before you start learning Pycord.

Resources:

Do buttons need any special permissions?​

No new permissions are needed for either the bot or the server to allow bots to use buttons.

Should I replace reactions with buttons for my bot?​

That is up to you. Buttons do provide a cleaner interface for your bot and are easier to use.