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.
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, requiresurlinstead ofcustom_id)
Link Buttons
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 menuplaceholder: Text shown when no option is selectedoptions: 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: 32768is set, you cannot usecontentorembedsat 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:17accent_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:10content: 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:9components: 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:14spacing:1(small) or2(large)divider: boolean — shows a horizontal line whentrue
{ "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:12items: array of media objects
Each media item:
media: object withurl(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:13file: object withurl(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
| Limit | Value |
|---|---|
| Top-level components per message | 40 |
| Components per Container | 10 |
| ActionRows per Container | 5 (max) |
| Items per MediaGallery | 10 |
| TextDisplay content length | 4000 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
| Limit | Value |
|---|---|
| Action rows per message | 5 |
| Buttons per action row | 5 |
| Select menus per action row | 1 |
| Total components per message | 25 |
| Select menu options | 2-25 |
| Button label length | 80 characters |
| Custom ID length | 100 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.
Related Articles
- Complete Guide to Discord Embed Formatting — Master Discord embed builder techniques including fields, colors, images, and character limits
- Discord Embed Colors - Complete Color Code Reference Guide — Master Discord embed colors with hex codes, decimal values, and color picker tools
- Discord Webhook Notifications and Automation - Complete Guide — Automate Discord notifications for server monitoring, CI/CD pipelines, and e-commerce alerts
- Best Discohook Alternative — Compare Discord webhook builders and find the best Discohook replacement
- Interactive Buttons and Actions — No-Code Bot Setup — Make buttons actually do things: assign roles, open forms, send DMs, and chain actions
Try it in our tool
Open Discord Webhook Builder