Guide

Embeds V2

Build rich Discord messages with the EmbedV2Builder.

Overview

glyria.js ships with a first-class builder for Discord's Components V2 — the new message system that replaces classic embeds with a fully composable layout.

const embed = new EmbedV2Builder()
  .container({ accentColor: 0x5865F2 })
    .textDisplay("# Hello!")
    .textDisplay("Welcome to the server.")
  .end()
  .build()

await ctx.reply({ ...embed })
Always spread .build() directly into your reply — not inside embeds: [].
// ✅ correct
await ctx.reply({ ...embed })

// ❌ incorrect
await ctx.reply({ embeds: [embed] })
EmbedV2Builder is available globally — no import needed.

Container

The container is the main building block. It wraps your content and supports an optional accent color:

new EmbedV2Builder()
  .container({ accentColor: 0x5865F2 })
    .textDisplay("Content here")
  .end()
  .build()
OptionTypeDescription
accentColornumberLeft border color (hex as number)
spoilerbooleanBlur content until clicked

Text display

Add markdown text anywhere inside a container:

.container()
  .textDisplay("# Title")
  .textDisplay("Some **bold** and *italic* text.")
.end()

Separator

Add visual spacing between sections:

.container()
  .textDisplay("Section one")
  .separator({ spacing: "large" })
  .textDisplay("Section two")
.end()
OptionValuesDescription
spacing"small" | "large"Spacing size
dividerbooleanShow a divider line

Section

A section pairs text with an accessory (button or thumbnail) on the right side:

.container()
  .section()
    .textDisplay("Choose your role")
    .buttonAccessory({ label: "Pick", customId: "role_picker", style: "primary" })
  .end()
.end()

With a thumbnail:

.section()
  .textDisplay("Server info")
  .thumbnailAccessory("https://example.com/icon.png", "Server icon")
.end()
A section requires at least one textDisplay and exactly one accessory (buttonAccessory or thumbnailAccessory). Missing either will throw an error.

Action row

Add clickable buttons below your content:

.container()
  .textDisplay("Are you sure?")
  .actionRow()
    .button({ label: "Confirm", customId: "confirm", style: "success" })
    .button({ label: "Cancel", customId: "cancel", style: "danger" })
  .end()
.end()

Button styles

StyleUse case
primaryMain action — requires customId
secondarySecondary action — requires customId
successPositive action — requires customId
dangerDestructive action — requires customId
linkExternal URL — requires url

Button options

OptionTypeDescription
labelstringButton text
customIdstringIdentifier for interaction handling
stylestringButton style
urlstringURL for link buttons
emojiobjectButton emoji
disabledbooleanDisable the button

Display up to 10 images in a grid:

.container()
  .textDisplay("Gallery")
  .mediaGallery([
    { url: "https://example.com/image1.png" },
    { url: "https://example.com/image2.png", description: "A caption" },
  ])
.end()

File

Attach a file to the message:

.container()
  .file("attachment://report.pdf")
.end()

Multiple containers

Chain multiple containers in a single message:

new EmbedV2Builder()
  .container({ accentColor: 0x57F287 })
    .textDisplay("✅ Action completed")
  .end()
  .separator()
  .container()
    .textDisplay("Here are the details...")
    .actionRow()
      .button({ label: "View", customId: "view_details", style: "secondary" })
    .end()
  .end()
  .build()

If configured in glyria.config.ts, a footer is automatically appended to every container:

// glyria.config.ts
export default defineConfig({
  theme: {
    embedV2: {
      footer: { text: "My Bot • v1.0" }
    }
  }
})

No extra code needed in your commands — glyria.js handles it automatically.

Full example

export default new GlyriaCommand()
  .setName("config")
  .setDescription("Bot configuration")
  .addSubCommandGroup((group) =>
    group
      .setName("logs")
      .setDescription("Logs configuration")
      .addSubCommand((cmd) =>
        cmd
          .setName("update")
          .setDescription("Update logs settings")
          .addBooleanOption((option) =>
            option
              .setName("enabled")
              .setDescription("Enable logs")
              .setRequired(true)
          )
          .execute(async (ctx) => {
            const embed = new EmbedV2Builder()
              .container({ accentColor: 0x5865F2 })
                .textDisplay("Logs configuration updated!")
                .separator({ spacing: "large" })
                .section()
                  .textDisplay("Disable logs")
                  .buttonAccessory({ label: "Disable", style: "link", url: "https://discord.com" })
                .end()
                .actionRow()
                  .button({ label: "Enable", style: "link", url: "https://discord.com" })
                .end()
              .end()
              .build()

            await ctx.reply({ ...embed })
          })
      )
  )
Copyright © 2026