Display Components
Overview
Components V2 provides display-oriented primitives in addition to legacy action rows: TextDisplay, Separator, Section, Thumbnail, MediaGallery, File, and Container. In discord.go, build these with the typed builders and set messages.FlagIsComponentsV2 on the response.
Tutorial: Build A V2 Layout
- Build text displays and other visual components.
- Compose them into a
SectionorContainer. - Add an action row when the layout needs an interactive control.
- Put the top-level components in
InteractionCallbackData.Components. - Set
Flags: messages.FlagIsComponentsV2.
V2 payloads are not ordinary content messages. Do not omit the flag, and do not assume a legacy content field is the right place for the primary layout.
Complete Runnable Example
Copy to examples/display-components/main.go, set DISCORD_TOKEN, and run it. Invoke /display to render a V2 container with a button accessory.
package main
import (
"log"
"os"
"github.com/discord-go/discord.go/bot"
"github.com/discord-go/discord.go/components"
"github.com/discord-go/discord.go/intents"
"github.com/discord-go/discord.go/interactions"
"github.com/discord-go/discord.go/messages"
)
func main() {
token := os.Getenv("DISCORD_TOKEN")
if token == "" {
log.Fatal("DISCORD_TOKEN is required")
}
router := bot.NewRouter()
router.Command("display", "Show a Components V2 layout", func(ctx *bot.InteractionContext) {
heading := components.NewTextDisplayBuilder().
SetContent("# Components V2\nA typed display layout built with discord.go.").
Build()
separator := components.NewSeparatorBuilder().
SetDivider(true).
SetSpacing(components.SeparatorSpacingSmall).
Build()
status := components.NewTextDisplayBuilder().
SetContent("The status is **ready**. Use the button to acknowledge this panel.").
Build()
section := components.NewSectionBuilder().
AddTextDisplayComponents(status).
SetButtonAccessory(components.NewButtonBuilder().
SetCustomID("display:ack").
SetLabel("Acknowledge").
SetStyle(components.ButtonStylePrimary).
Build()).
Build()
container := components.NewContainerBuilder().
SetAccentColor(0x5865F2).
AddTextDisplayComponents(heading).
AddSeparatorComponents(separator).
AddSectionComponents(section).
Build()
if err := ctx.ReplyComplex(&interactions.InteractionCallbackData{
Flags: messages.FlagIsComponentsV2,
Components: []components.Component{container},
}); err != nil {
log.Printf("display response: %v", err)
}
})
router.Button("display:ack", func(ctx *bot.InteractionContext) {
if err := ctx.UpdateContent("The panel was acknowledged."); err != nil {
log.Printf("display update: %v", err)
}
})
b := bot.New(token, bot.WithIntents(intents.Guilds), bot.WithRouter(router))
if err := b.Run(); err != nil {
log.Fatal(err)
}
}Components V2 And Legacy Components
V2 display components can be composed with the V2 builders. Interactive menus and buttons remain action-row components in this repository. A V2 Section can use a button or thumbnail accessory, and a V2 Container can contain sections, separators, text displays, galleries, files, and action rows through its typed builder methods.
When attaching a file, build a File component with an attachment://filename URL and upload a matching rest.File in the same multipart response. examples/v2_components/main.go is the repository's source-backed file example.
Common Mistakes
Wrong:
_ = ctx.ReplyComplex(&interactions.InteractionCallbackData{
Components: []components.Component{container},
})Correct:
_ = ctx.ReplyComplex(&interactions.InteractionCallbackData{
Flags: messages.FlagIsComponentsV2,
Components: []components.Component{container},
})Also avoid using unstable user input as attachment names or external media URLs. Validate names, sizes, and URLs before constructing a multipart request.
Expected Result
/display returns a V2 container with a heading, divider, status section, and button accessory. Clicking the button acknowledges the component interaction.