Skip to main content
All articles
· Updated February 17, 2026

Discord Components V2: Buttons, Selects, Containers, and Sections in Webhooks

Complete guide to Discord's Components V2 API. Learn how to use Containers, Sections, TextDisplay, MediaGallery, Separators, and interactive buttons and select menus in webhook messages.

discord apicomponentsbuttonsinteractivediscord components 2026containerssections
Discord Components V2: Buttons, Selects, Containers, and Sections in Webhooks

What Are Discord Components V2?

Discord Components V2 is the API for adding interactive elements to messages. Components include:

  • Buttons: Clickable buttons with labels, colors, and custom IDs
  • Select Menus: Dropdown menus with multiple options
  • Text Inputs: Single-line or multi-line text fields (modal forms only)

Components transform static messages into interactive interfaces. Users can click buttons, select options, or fill out forms without leaving Discord.

Important limitation: Webhooks can send messages with buttons and select menus, but they cannot receive interaction events. To handle button clicks or menu selections, you need a Discord bot with interaction handlers.

This guide focuses on sending component-based messages via webhooks for display purposes.

Button Components

Buttons appear below the message content in rows (action rows). Each button has a style, label, and either a custom ID or URL.

Basic Button Example

{
  "content": "Choose an option:",
  "components": [
    {
      "type": 1,
      "components": [
        {
          "type": 2,
          "style": 1,
          "label": "Primary",
          "custom_id": "primary_button"
        },
        {
          "type": 2,
          "style": 2,
          "label": "Secondary",
          "custom_id": "secondary_button"
        },
        {
          "type": 2,
          "style": 3,
          "label": "Success",
          "custom_id": "success_button"
        },
        {
          "type": 2,
          "style": 4,
          "label": "Danger",
          "custom_id": "danger_button"
        }
      ]
    }
  ]
}

Component types:

  • type: 1 = Action Row (container for buttons/selects)
  • type: 2 = Button

Button styles:

  • 1 = Primary (blurple)
  • 2 = Secondary (gray)
  • 3 = Success (green)
  • 4 = Danger (red)
  • 5 = Link (gray, requires url instead of custom_id)

Link buttons open URLs when clicked:

{
  "content": "Check out these resources:",
  "components": [
    {
      "type": 1,
      "components": [
        {
          "type": 2,
          "style": 5,
          "label": "Documentation",
          "url": "https://discord.com/developers/docs"
        },
        {
          "type": 2,
          "style": 5,
          "label": "GitHub",
          "url": "https://github.com"
        }
      ]
    }
  ]
}

Link buttons (style 5) use url instead of custom_id and don’t trigger interaction events.

Buttons with Emojis

Add emojis to buttons for visual appeal:

{
  "content": "React to this message:",
  "components": [
    {
      "type": 1,
      "components": [
        {
          "type": 2,
          "style": 3,
          "label": "Like",
          "custom_id": "like_button",
          "emoji": {
            "name": "👍"
          }
        },
        {
          "type": 2,
          "style": 4,
          "label": "Dislike",
          "custom_id": "dislike_button",
          "emoji": {
            "name": "👎"
          }
        }
      ]
    }
  ]
}

For custom emojis, use "emoji": {"id": "123456789", "name": "custom_emoji"}.

Disabled Buttons

Disable buttons to make them non-clickable:

{
  "content": "This poll has ended:",
  "components": [
    {
      "type": 1,
      "components": [
        {
          "type": 2,
          "style": 2,
          "label": "Option A (45%)",
          "custom_id": "option_a",
          "disabled": true
        },
        {
          "type": 2,
          "style": 2,
          "label": "Option B (55%)",
          "custom_id": "option_b",
          "disabled": true
        }
      ]
    }
  ]
}

Disabled buttons appear grayed out and cannot be clicked.

Multiple Button Rows

Action rows can contain up to 5 buttons each. Use multiple action rows for more buttons:

{
  "content": "Select a category:",
  "components": [
    {
      "type": 1,
      "components": [
        {
          "type": 2,
          "style": 1,
          "label": "Technology",
          "custom_id": "cat_tech"
        },
        {
          "type": 2,
          "style": 1,
          "label": "Science",
          "custom_id": "cat_science"
        },
        {
          "type": 2,
          "style": 1,
          "label": "Arts",
          "custom_id": "cat_arts"
        }
      ]
    },
    {
      "type": 1,
      "components": [
        {
          "type": 2,
          "style": 1,
          "label": "Sports",
          "custom_id": "cat_sports"
        },
        {
          "type": 2,
          "style": 1,
          "label": "Gaming",
          "custom_id": "cat_gaming"
        },
        {
          "type": 2,
          "style": 1,
          "label": "Music",
          "custom_id": "cat_music"
        }
      ]
    }
  ]
}

Limits:

  • Maximum 5 action rows per message
  • Maximum 5 buttons per action row
  • Maximum 25 components total per message

Select Menu Components

Select menus (dropdowns) let users choose from a list of options.

Basic Select Menu

{
  "content": "Choose your favorite programming language:",
  "components": [
    {
      "type": 1,
      "components": [
        {
          "type": 3,
          "custom_id": "language_select",
          "placeholder": "Select a language...",
          "options": [
            {
              "label": "JavaScript",
              "value": "js",
              "description": "The language of the web",
              "emoji": {
                "name": "🟨"
              }
            },
            {
              "label": "Python",
              "value": "py",
              "description": "Simple and powerful",
              "emoji": {
                "name": "🐍"
              }
            },
            {
              "label": "Rust",
              "value": "rs",
              "description": "Fast and memory-safe",
              "emoji": {
                "name": "🦀"
              }
            },
            {
              "label": "Go",
              "value": "go",
              "description": "Concurrent and efficient",
              "emoji": {
                "name": "🔵"
              }
            }
          ]
        }
      ]
    }
  ]
}

Component type:

  • type: 3 = String Select Menu

Select menu properties:

  • custom_id: Unique identifier for the select menu
  • placeholder: Text shown when no option is selected
  • options: Array of selectable options (2-25 options)
  • min_values: Minimum selections required (default: 1)
  • max_values: Maximum selections allowed (default: 1)

Multi-Select Menu

Allow users to select multiple options:

{
  "content": "Select your interests (up to 3):",
  "components": [
    {
      "type": 1,
      "components": [
        {
          "type": 3,
          "custom_id": "interests_select",
          "placeholder": "Choose your interests...",
          "min_values": 1,
          "max_values": 3,
          "options": [
            {
              "label": "Gaming",
              "value": "gaming"
            },
            {
              "label": "Music",
              "value": "music"
            },
            {
              "label": "Art",
              "value": "art"
            },
            {
              "label": "Technology",
              "value": "tech"
            },
            {
              "label": "Sports",
              "value": "sports"
            }
          ]
        }
      ]
    }
  ]
}

Set min_values and max_values to control selection limits.

Select Menu with Default Values

Pre-select options using the default property:

{
  "content": "Update your notification preferences:",
  "components": [
    {
      "type": 1,
      "components": [
        {
          "type": 3,
          "custom_id": "notifications_select",
          "placeholder": "Select notification types...",
          "max_values": 3,
          "options": [
            {
              "label": "Mentions",
              "value": "mentions",
              "default": true
            },
            {
              "label": "Direct Messages",
              "value": "dms",
              "default": true
            },
            {
              "label": "Server Updates",
              "value": "updates",
              "default": false
            }
          ]
        }
      ]
    }
  ]
}

Options with "default": true appear pre-selected.

Combining Buttons and Select Menus

Mix buttons and select menus in the same message:

{
  "content": "Configure your settings:",
  "components": [
    {
      "type": 1,
      "components": [
        {
          "type": 3,
          "custom_id": "theme_select",
          "placeholder": "Choose a theme...",
          "options": [
            {
              "label": "Light",
              "value": "light"
            },
            {
              "label": "Dark",
              "value": "dark"
            },
            {
              "label": "Auto",
              "value": "auto"
            }
          ]
        }
      ]
    },
    {
      "type": 1,
      "components": [
        {
          "type": 2,
          "style": 3,
          "label": "Save",
          "custom_id": "save_button"
        },
        {
          "type": 2,
          "style": 4,
          "label": "Cancel",
          "custom_id": "cancel_button"
        }
      ]
    }
  ]
}

Each action row can contain either buttons (up to 5) or a single select menu.

Components with Embeds

Combine components with rich embeds:

{
  "embeds": [
    {
      "title": "Server Rules",
      "description": "Please read and accept our server rules to continue.",
      "color": 5793266,
      "fields": [
        {
          "name": "Rule 1",
          "value": "Be respectful to all members"
        },
        {
          "name": "Rule 2",
          "value": "No spam or self-promotion"
        },
        {
          "name": "Rule 3",
          "value": "Keep content appropriate"
        }
      ]
    }
  ],
  "components": [
    {
      "type": 1,
      "components": [
        {
          "type": 2,
          "style": 3,
          "label": "I Accept",
          "custom_id": "accept_rules"
        },
        {
          "type": 2,
          "style": 5,
          "label": "Read Full Rules",
          "url": "https://example.com/rules"
        }
      ]
    }
  ]
}

This pattern is common for verification systems, announcements, and interactive help messages.

Real-World Example: Status Dashboard

Create an interactive status dashboard with buttons:

{
  "embeds": [
    {
      "title": "System Status Dashboard",
      "description": "All services are operational",
      "color": 5763719,
      "fields": [
        {
          "name": "API Server",
          "value": "✅ Online",
          "inline": true
        },
        {
          "name": "Database",
          "value": "✅ Online",
          "inline": true
        },
        {
          "name": "CDN",
          "value": "✅ Online",
          "inline": true
        }
      ],
      "footer": {
        "text": "Last updated"
      },
      "timestamp": "2025-04-12T14:30:00.000Z"
    }
  ],
  "components": [
    {
      "type": 1,
      "components": [
        {
          "type": 2,
          "style": 5,
          "label": "View Details",
          "url": "https://status.example.com"
        },
        {
          "type": 2,
          "style": 5,
          "label": "Subscribe to Updates",
          "url": "https://status.example.com/subscribe"
        }
      ]
    }
  ]
}

Sending Components via Webhook (JavaScript)

const WEBHOOK_URL = "https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN";

async function sendComponentMessage() {
  const payload = {
    content: "Choose an action:",
    components: [
      {
        type: 1,
        components: [
          {
            type: 2,
            style: 1,
            label: "Option 1",
            custom_id: "option_1"
          },
          {
            type: 2,
            style: 2,
            label: "Option 2",
            custom_id: "option_2"
          }
        ]
      }
    ]
  };

  const response = await fetch(WEBHOOK_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(payload)
  });

  return response.ok;
}

sendComponentMessage();

Sending Components via curl

curl -H "Content-Type: application/json" \
     -d '{
       "content": "Click a button:",
       "components": [
         {
           "type": 1,
           "components": [
             {
               "type": 2,
               "style": 5,
               "label": "Visit Website",
               "url": "https://discord-webhook.com"
             }
           ]
         }
       ]
     }' \
     https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN

The Real Components V2: Layout Components

The sections above cover buttons and select menus, which Discord has supported for a while. The actual “Components V2” update introduced a completely different set of layout components: Containers, Sections, TextDisplay, Separators, MediaGallery, and File. These let you build rich, structured message layouts without embeds.

Components V2 Message Format

To use the new layout components, you must set flags: 32768 on the message. This flag is called IS_COMPONENTS_V2 and tells Discord to render the message using the new component system.

In V2 mode, the top-level components array can contain:

  • Container (type 17) — a styled wrapper block
  • TextDisplay (type 10) — markdown text
  • Separator (type 14) — a visual divider
  • MediaGallery (type 12) — a grid of images or videos
  • File (type 13) — an attached file display
  • ActionRow (type 1) — buttons and select menus, same as before
{
  "flags": 32768,
  "components": [
    {
      "type": 10,
      "content": "**Welcome!** This message uses Components V2."
    }
  ]
}

Note: When flags: 32768 is set, you cannot use content or embeds at the top level. All content goes inside components.

Container Component (type 17)

A Container is a visual block that groups other components together. It supports an optional accent color (same integer format as embed colors) and a spoiler toggle.

Properties:

  • type: 17
  • accent_color: integer color value (optional)
  • spoiler: boolean, hides content behind a spoiler (optional)
  • components: array of child components

Allowed children: TextDisplay, Separator, Section, MediaGallery, ActionRow, File

{
  "flags": 32768,
  "components": [
    {
      "type": 17,
      "accent_color": 5793266,
      "spoiler": false,
      "components": [
        { "type": 10, "content": "Hello **world**!" },
        { "type": 14, "spacing": 1, "divider": true },
        { "type": 10, "content": "Second paragraph" }
      ]
    }
  ]
}

The accent_color renders as a colored left border, similar to embed colors. Use our color reference to find the right integer value.

TextDisplay Component (type 10)

The simplest V2 component. It renders a block of text with full Discord markdown support.

Properties:

  • type: 10
  • content: string with markdown
{ "type": 10, "content": "**Bold**, *italic*, `code`, and [links](https://discord-webhook.com)" }

TextDisplay supports all standard Discord markdown: bold, italic, underline, strikethrough, code blocks, block quotes, and links.

Section Component (type 9)

A Section places text on the left with an accessory (a Thumbnail image or a Button) on the right. Good for profile cards, item listings, or any layout that pairs text with a visual.

Properties:

  • type: 9
  • components: array of TextDisplay components (the left side)
  • accessory: a single Thumbnail (type 11) or Button (type 2) object
{
  "type": 9,
  "components": [
    { "type": 10, "content": "**Server Stats**" },
    { "type": 10, "content": "Members: 1,204\nOnline: 87" }
  ],
  "accessory": {
    "type": 11,
    "media": {
      "url": "https://cdn.discordapp.com/icons/SERVER_ID/icon.png"
    }
  }
}

Separator Component (type 14)

A Separator adds a visual break between components. You control the spacing size and whether a visible divider line appears.

Properties:

  • type: 14
  • spacing: 1 (small) or 2 (large)
  • divider: boolean — shows a horizontal line when true
{ "type": 14, "spacing": 1, "divider": true }

Use Separators to break up long Containers into readable sections without needing multiple Containers.

MediaGallery Component (type 12)

MediaGallery displays a grid of images or videos. Each item in the gallery has its own URL, description, and optional spoiler.

Properties:

  • type: 12
  • items: array of media objects

Each media item:

  • media: object with url (string)
  • description: alt text / caption (optional)
  • spoiler: boolean (optional)
{
  "type": 12,
  "items": [
    {
      "media": { "url": "https://example.com/screenshot1.png" },
      "description": "Dashboard overview"
    },
    {
      "media": { "url": "https://example.com/screenshot2.png" },
      "description": "Settings panel",
      "spoiler": false
    }
  ]
}

File Component (type 13)

A File component displays an uploaded attachment inline in the message layout.

Properties:

  • type: 13
  • file: object with url (attachment URL)
  • spoiler: boolean (optional)
{
  "type": 13,
  "file": { "url": "attachment://report.pdf" },
  "spoiler": false
}

The url must reference an attachment uploaded with the message using multipart form data.

Full V2 Message Example

Here’s a complete message combining multiple V2 components:

{
  "flags": 32768,
  "components": [
    {
      "type": 17,
      "accent_color": 5763719,
      "components": [
        {
          "type": 9,
          "components": [
            { "type": 10, "content": "## Deployment Complete" },
            { "type": 10, "content": "**Service**: api-gateway\n**Version**: v2.4.1\n**Environment**: Production" }
          ],
          "accessory": {
            "type": 11,
            "media": { "url": "https://example.com/success-icon.png" }
          }
        },
        { "type": 14, "spacing": 1, "divider": true },
        {
          "type": 12,
          "items": [
            {
              "media": { "url": "https://example.com/deploy-graph.png" },
              "description": "Response time after deploy"
            }
          ]
        },
        { "type": 14, "spacing": 1, "divider": false },
        {
          "type": 1,
          "components": [
            {
              "type": 2,
              "style": 5,
              "label": "View Logs",
              "url": "https://logs.example.com"
            },
            {
              "type": 2,
              "style": 5,
              "label": "Rollback",
              "url": "https://deploy.example.com/rollback"
            }
          ]
        }
      ]
    }
  ]
}

This produces a single styled block with a section header, a media image, a divider, and link buttons — all without using embeds.

Components V2 Limits

LimitValue
Top-level components per message40
Components per Container10
ActionRows per Container5 (max)
Items per MediaGallery10
TextDisplay content length4000 characters

Building V2 Messages Visually

Composing nested JSON by hand gets tedious fast. Our Discord Webhook Builder supports all Components V2 types — Containers, Sections, TextDisplay, Separators, MediaGallery, and more. Build your layout visually, preview it in real time, then copy the JSON straight into your code.

Limitations and Best Practices

Webhooks cannot handle interactions: Webhooks can send component messages but cannot receive button clicks or menu selections. You need a Discord bot with interaction handlers to respond to user actions.

Use link buttons for webhooks: Since webhooks can’t handle interactions, link buttons (style 5) are the most practical for webhook-sent messages.

Keep labels concise: Button labels have a 80-character limit. Keep them short and clear.

Provide context: Always include message content or an embed explaining what the components do.

Test layouts: Component layouts can look different on mobile vs desktop. Test your designs on both.

Disable expired components: If a poll or form has closed, edit the message to disable all buttons/selects.

Use emojis wisely: Emojis make buttons more visually appealing but can clutter the interface if overused.

Want buttons that actually respond to clicks? Standard webhooks can only send buttons — they can’t handle interactions. But with discord-webhook.com’s interactive action system, your buttons can assign roles, open forms, send DMs, and trigger multi-step automations. No custom bot code required.

Component Limits Summary

LimitValue
Action rows per message5
Buttons per action row5
Select menus per action row1
Total components per message25
Select menu options2-25
Button label length80 characters
Custom ID length100 characters

Next Steps

You now understand how to add interactive components to Discord webhook messages. While webhooks can’t handle interactions, components still provide visual structure and link buttons for navigation.

To design component-based messages visually, try our free Discord Webhook Builder. Build your layout with buttons and select menus, preview it in real-time, then export the JSON for use in your applications.