Documentation Index
Fetch the complete documentation index at: https://mintlify.com/disgoorg/disgo/llms.txt
Use this file to discover all available pages before exploring further.
Message components allow you to create interactive elements like buttons and select menus that users can click on directly in Discord messages.
Buttons are clickable components that can trigger actions in your bot.
discord.NewPrimaryButton("Click me!", "button_id")
Buttons must be placed inside an action row:
_, err := client.Rest.CreateMessage(channelID, discord.MessageCreate{
Content: "Choose an option:",
Components: []discord.LayoutComponent{
discord.NewActionRow(
discord.NewPrimaryButton("Option 1", "option_1"),
discord.NewPrimaryButton("Option 2", "option_2"),
discord.NewDangerButton("Cancel", "cancel"),
),
},
})
Each action row can contain up to 5 buttons. You can have up to 5 action rows per message.
Listen for button interaction events:
bot.WithEventListenerFunc(func(event *events.ComponentInteractionCreate) {
if event.ButtonInteractionData().CustomID() == "option_1" {
_ = event.CreateMessage(discord.MessageCreate{
Content: "You selected Option 1!",
Flags: discord.MessageFlagEphemeral,
})
}
})
package main
import (
"context"
"log/slog"
"os"
"os/signal"
"syscall"
"github.com/disgoorg/disgo"
"github.com/disgoorg/disgo/bot"
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events"
"github.com/disgoorg/disgo/gateway"
)
var token = os.Getenv("disgo_token")
func main() {
client, err := disgo.New(token,
bot.WithGatewayConfigOpts(
gateway.WithIntents(
gateway.IntentGuilds,
gateway.IntentGuildMessages,
),
),
bot.WithEventListenerFunc(func(event *events.MessageCreate) {
if event.Message.Author.Bot {
return
}
if event.Message.Content == "buttons" {
_, _ = event.Client().Rest.CreateMessage(
event.ChannelID,
discord.MessageCreate{
Content: "Choose an action:",
Components: []discord.LayoutComponent{
discord.NewActionRow(
discord.NewSuccessButton("Approve", "approve"),
discord.NewDangerButton("Reject", "reject"),
),
},
},
)
}
}),
bot.WithEventListenerFunc(func(event *events.ComponentInteractionCreate) {
customID := event.ButtonInteractionData().CustomID()
var response string
if customID == "approve" {
response = "✅ Approved!"
} else if customID == "reject" {
response = "❌ Rejected!"
}
_ = event.CreateMessage(discord.MessageCreate{
Content: response,
Flags: discord.MessageFlagEphemeral,
})
}),
)
if err != nil {
slog.Error("error while building bot", slog.Any("err", err))
return
}
defer client.Close(context.TODO())
if err = client.OpenGateway(context.TODO()); err != nil {
slog.Error("error while connecting to gateway", slog.Any("err", err))
return
}
slog.Info("bot is running. Press CTRL-C to exit.")
s := make(chan os.Signal, 1)
signal.Notify(s, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
<-s
}
Select menus allow users to choose from a dropdown list of options.
The most common type of select menu with custom string values:
discord.NewStringSelectMenu(
"color_select",
"Choose a color",
discord.NewStringSelectMenuOption("Red", "red"),
discord.NewStringSelectMenuOption("Blue", "blue"),
discord.NewStringSelectMenuOption("Green", "green"),
)
Allows selecting Discord users:
discord.NewUserSelectMenu("user_select", "Select a user")
Allows selecting Discord roles:
discord.NewRoleSelectMenu("role_select", "Select a role")
Allows selecting Discord channels:
discord.NewChannelSelectMenu("channel_select", "Select a channel")
Allows selecting users or roles:
discord.NewMentionableSelectMenu("mention_select", "Select a user or role")
Setting min and max values
Control how many options users can select:
discord.NewStringSelectMenu(
"multi_select",
"Choose your favorite colors",
discord.NewStringSelectMenuOption("Red", "red"),
discord.NewStringSelectMenuOption("Blue", "blue"),
discord.NewStringSelectMenuOption("Green", "green"),
discord.NewStringSelectMenuOption("Yellow", "yellow"),
).WithMinValues(1).WithMaxValues(3)
Handling select menu interactions
bot.WithEventListenerFunc(func(event *events.ComponentInteractionCreate) {
data := event.StringSelectMenuInteractionData()
if data.CustomID() == "color_select" {
selected := data.Values[0]
_ = event.CreateMessage(discord.MessageCreate{
Content: "You selected: " + selected,
Flags: discord.MessageFlagEphemeral,
})
}
})
Updating components
You can update the message with new components after an interaction:
_ = event.UpdateMessage(discord.MessageUpdate{
Content: discord.NewNullablePtr("Updated!"),
Components: &[]discord.LayoutComponent{
discord.NewActionRow(
discord.NewSecondaryButton("Done", "done").WithDisabled(true),
),
},
})
Disabling components
Disable components to prevent further interactions:
discord.NewPrimaryButton("Already Clicked", "button_id").WithDisabled(true)
Adding emojis to components
Make your components more visually appealing with emojis:
discord.NewPrimaryButton("Like", "like_button").WithEmoji(
discord.ComponentEmoji{Name: "👍"},
)
// Or with custom emoji
discord.NewPrimaryButton("Custom", "custom_button").WithEmoji(
discord.ComponentEmoji{ID: snowflake.ID(123456789)},
)
Components v2
DisGo supports Discord’s new Components v2 system with enhanced layouts:
discord.MessageCreate{
Flags: discord.MessageFlagIsComponentsV2,
Components: []discord.LayoutComponent{
discord.NewContainer(
discord.NewSection(
discord.NewTextDisplay("**Song Info**"),
discord.NewTextDisplay("Artist: Example"),
),
discord.NewActionRow(
discord.NewPrimaryButton("", "play").WithEmoji(
discord.ComponentEmoji{Name: "▶️"},
),
discord.NewPrimaryButton("", "pause").WithEmoji(
discord.ComponentEmoji{Name: "⏸️"},
),
),
),
},
}
Components v2 is a newer Discord feature. Make sure to set the MessageFlagIsComponentsV2 flag when using v2 components.
Using the handler package
The handler package provides cleaner component routing:
import "github.com/disgoorg/disgo/handler"
r := handler.New()
r.Component("/approve/{id}", handleApprove)
r.Component("/reject/{id}", handleReject)
func handleApprove(event *handler.ComponentEvent) error {
id := event.Vars["id"]
return event.CreateMessage(discord.MessageCreate{
Content: "Approved item: " + id,
Flags: discord.MessageFlagEphemeral,
})
}
Best practices
- Use custom IDs wisely: Make them descriptive and consider including data in the ID (e.g.,
delete_message_123456)
- Always respond to interactions: Discord requires a response within 3 seconds
- Use ephemeral messages: For confirmations or error messages that don’t need to be public
- Disable components after use: Prevent duplicate actions by disabling buttons after they’re clicked
- Handle timeouts: Components become invalid after 15 minutes - handle expired interactions gracefully
- Limit action rows: Don’t overwhelm users with too many buttons
Next steps