Category: Discord Bot Tutorials

Discord Bot Tutorial#5Implementing Button Interactions!

This post was translated from the Korean original.

Contents8 sections

Hi there! This is Mojangcam 😎

In Part #4, we sent embeds with slash commands, right? This time we're going one step further and adding clickable buttons to our messages! Let's build a truly "interactive bot" that responds when someone clicks a button.

Once you're done, you'll be able to do things like:

  • Give users a choice with ✅ Confirm / ❌ Cancel buttons
  • One-click features like 🎫 creating tickets or granting roles
  • Send replies that only the person who clicked the button can see

📌 What You Need

  • Python 3.8 or later
  • discord.py 2.x (buttons are supported starting from 2.0!)
  • A bot token + the bot already invited to your server (see Part 1!)
Terminal
pip install -U discord.py

If your version is older, make sure to update it. You can check your version with pip show discord.py.

📌 Step 1 - The Core of Buttons: Understanding the View

In discord.py, you can't use a button on its own. You put it in a container called a View and send it along with a message.

  • discord.ui.View → a box that holds buttons, dropdowns, and so on 📦
  • The discord.ui.button decorator → creates a button inside the View and connects the function to run when it's clicked

Let's look at the structure first!

Python
import discord

class MyView(discord.ui.View):
    @discord.ui.button(label="Click me!", style=discord.ButtonStyle.primary)
    async def my_button(self, interaction: discord.Interaction, button: discord.ui.Button):
        await interaction.response.send_message("The button was clicked!")

⚠️ The callback's parameter order is (self, interaction, button). It's the reverse of examples from other libraries (like pycord), so it's easy to mix up. Watch out!

📌 Step 2 - Getting to Know Button Styles

There are 5 button colors you can pass to style=.

Style Color Example use
ButtonStyle.primary Blue Main action
ButtonStyle.secondary Gray Secondary action
ButtonStyle.success Green Confirm/accept
ButtonStyle.danger Red Delete/reject
ButtonStyle.link Link Opens a URL (needs url=, no callback)

📌 Step 3 - Building Confirm/Cancel Buttons

Let's build the Confirm/Cancel pattern, the one you'll use most in real projects.

Python
class ConfirmView(discord.ui.View):
    def __init__(self):
        super().__init__(timeout=60)  # disable the buttons after 60 seconds

    @discord.ui.button(label="Confirm", style=discord.ButtonStyle.success)
    async def confirm(self, interaction: discord.Interaction, button: discord.ui.Button):
        await interaction.response.edit_message(content="✅ Confirmed!", view=None)

    @discord.ui.button(label="Cancel", style=discord.ButtonStyle.danger)
    async def cancel(self, interaction: discord.Interaction, button: discord.ui.Button):
        await interaction.response.edit_message(content="❌ Cancelled.", view=None)

Key points 💡

  • timeout=60 : after 60 seconds, the buttons stop working (the default is 180 seconds)
  • edit_message(..., view=None) : when a button is clicked, it edits the original message and removes the buttons
  • interaction.response can respond exactly once, within 3 seconds. If you need to send more, use interaction.followup.send()!

📌 Step 4 - Full Code (Copy-Paste OK)

Here's the finished version combined with a slash command.

Python
import discord
from discord.ext import commands

intents = discord.Intents.default()
bot = commands.Bot(command_prefix="/", intents=intents)

class ConfirmView(discord.ui.View):
    def __init__(self):
        super().__init__(timeout=60)

    @discord.ui.button(label="Confirm", style=discord.ButtonStyle.success)
    async def confirm(self, interaction: discord.Interaction, button: discord.ui.Button):
        await interaction.response.edit_message(content="✅ Confirmed!", view=None)

    @discord.ui.button(label="Cancel", style=discord.ButtonStyle.danger)
    async def cancel(self, interaction: discord.Interaction, button: discord.ui.Button):
        await interaction.response.edit_message(content="❌ Cancelled.", view=None)

@bot.event
async def on_ready():
    await bot.tree.sync()
    print(f"{bot.user} bot is ready!")

@bot.tree.command(name="확인", description="Sends Confirm/Cancel buttons")
async def confirm_command(interaction: discord.Interaction):
    await interaction.response.send_message("Shall we proceed?", view=ConfirmView())

bot.run("Token")

📌 Checking the Result

Run the bot and type /확인 (confirm) in a Discord channel, and you'll see the "Shall we proceed?" message along with a green Confirm button and a red Cancel button! When you click a button, the message changes to "✅ Confirmed!" and the buttons disappear.

📌 Common Errors

1. I clicked the button and it says "This interaction failed"

This happens when the callback doesn't respond within 3 seconds. If your processing takes a while, call await interaction.response.defer() first.

2. TypeError: ... missing 1 required positional argument

This is a callback parameter order problem! In discord.py 2.x, the order is (self, interaction, button). If you write (self, button, interaction), you'll get this error.

3. After restarting the bot, the buttons on older messages don't work

By default, a View only exists in memory. To keep it working after a restart, you need to create a persistent view with timeout=None + a custom_id, and register it with bot.add_view(). (I'll cover this in an advanced part later!)

📌 Wrapping Up

Today we implemented click interactions using discord.py's View and buttons.

  • Buttons are sent inside a View
  • 5 styles (primary/secondary/success/danger/link)
  • Respond within 3 seconds, and only once!

In the next part, we'll build a dropdown (Select Menu). Stay tuned 🙌

📚 Discord Bot Tutorial Series

  1. Mastering Slash Commands
  2. Adding Options (Arguments) to Slash Commands
  3. Adding Subcommands to Slash Commands
  4. Sending Embeds with Slash Commands
  5. Implementing Button Interactions ← You are here!

Series · Part 5 of 32

Discord Bot Tutorial

  1. #1Discord Bot Tutorial - Mastering Slash Commands!
  2. #2Adding Options (Arguments) to Slash Commands!
  3. #3Adding Subcommands to Slash Commands!
  4. #4Sending Embeds with Slash Commands!
  5. #5Implementing Button Interactions!You are here
  6. #6Implementing a Dropdown (Select Menu)!
  7. #7Getting User Input with a Modal (Python Example)
  8. #8Styling Embeds with Images and Fields (Python Example)
  9. #9Connecting AI (Claude & ChatGPT API) to Your Bot! (Python Example)
  10. #10Adding Multi-Language (i18n) Support! (Python Example)
  11. #11Building a Web Dashboard with Flask! (Python Example)
  12. #12Managing Command Cooldowns and Permissions! (Python Example)
  13. #13Automating Roles and Channels to Manage Your Server! (Python Example)
  14. #14Saving Data! JSON vs SQLite (Python Example)
  15. #15슬래시 명령어 그룹(Group)으로 명령어 정리하기! (파이썬 예제) (Korean)
  16. #16리마인더·알림 기능 만들기! 봇이 먼저 말 걸게 하기 (파이썬 예제) (Korean)
  17. #17RAG로 문서 기반 답변봇 만들기! 우리 서버 전용 FAQ 봇 (파이썬 예제) (Korean)
  18. #18투표(Poll) 기능 만들기! 다수결로 정하는 서버 규칙 (파이썬 예제) (Korean)
  19. #19Cog로 코드 쪼개기! 1000줄 봇도 안 더러워지는 구조 (파이썬 예제) (Korean)
  20. #20자동 환영 메시지·온보딩 만들기! 규칙 동의 버튼으로 자동 역할 부여 (파이썬 예제) (Korean)
  21. #21키워드 자동응답·금지어 필터 만들기! 경고 누적으로 자동 제재까지 (파이썬 예제) (Korean)
  22. #22슬래시 명령어 자동완성(Autocomplete) 만들기! 금지어를 골라서 추가·삭제 (파이썬 예제) (Korean)
  23. #23aiosqlite로 비동기 DB 제대로 설계하기! JSON 탈출 (파이썬 예제) (Korean)
  24. #24채널 난독화(Channel Obfuscation) 대응하기! 11/16부터 안 보이는 채널이 생겨요 (파이썬 예제) (Korean)
  25. #25봇이 죽지 않게! 에러 핸들링·로깅 체계 잡기 (파이썬 예제) (Korean)
  26. #26티켓(Ticket) 문의 시스템 만들기! 버튼 하나로 1:1 상담 채널 자동 생성 (파이썬 예제) (Korean)
  27. #27티켓 담당자 배정·우선순위 만들기! 스태프가 여러 명이어도 안 꼬이게 (파이썬 예제) (Korean)
  28. #28레벨·경험치(XP) 시스템 만들기! 채팅할수록 레벨업, 랭킹판까지 (파이썬 예제) (Korean)
  29. #29화폐·상점(Economy) 시스템 만들기! 코인 벌어서 아이템 사기 (파이썬 예제) (Korean)
  30. #30Build a Poll & Survey Bot! Native Polls + Buttons with Real-Time Tallies (Python Example) 🗳️
  31. #32Build a Server Log & Audit Log Channel! Auto-Log Message Deletes, Edits, Joins/Leaves, and Kicks (Python Example) 🕵️
  32. #33임시 음성방 확장, 파티방 만들기! 방장 위임·강퇴·초대 전용(비공개) 방 + 전용 채팅방 자동 생성 (파이썬 예제) 🎉 (Korean)
Show all 32Collapse

Author

Mojangcam

A hands-on dev and IT log: code I write, servers I run, and the errors I fix along the way.