Content Components (Components v2)
What this skill does
Reference for displaying static content using Text Display, Thumbnail, Media Gallery, and File components.
When to use
- •Display formatted text with Markdown
- •Show images and thumbnails
- •Display media galleries
- •Embed file attachments
- •Replace legacy embed fields with modern components
Text Display (Type 10)
What is Text Display?
The primary component for displaying text content. Replaces message content field and embed descriptions.
Supports Markdown: Bold, italic, code blocks, headings, links, etc.
Creating Text Display
Basic Text
use poise::serenity_prelude as serenity;
serenity::CreateTextDisplay::new("Hello World!")
Markdown Text
serenity::CreateTextDisplay::new("**Bold** *italic* `code`")
Headings
// H1
serenity::CreateTextDisplay::new("# Main Title")
// H2
serenity::CreateTextDisplay::new("## Subtitle")
// H3
serenity::CreateTextDisplay::new("### Section")
Multi-line Text
serenity::CreateTextDisplay::new(format!(
"**Status**: Enabled\n\
**Users**: 42\n\
**Last Updated**: <t:{}:R>",
timestamp
))
Dynamic Content
serenity::CreateTextDisplay::new(format!(
"Welcome <@{}>!\nYour role: {}",
user_id,
role_name
))
Usage in Container
let mut components = vec![];
// Title
components.push(serenity::CreateContainerComponent::TextDisplay(
serenity::CreateTextDisplay::new("## Configuration")
));
// Description
components.push(serenity::CreateContainerComponent::TextDisplay(
serenity::CreateTextDisplay::new("Configure your server settings below.")
));
// Field-style content
components.push(serenity::CreateContainerComponent::TextDisplay(
serenity::CreateTextDisplay::new(format!(
"**Guild ID**: `{}`\n\
**Owner**: <@{}>\n\
**Members**: {}",
guild_id, owner_id, member_count
))
));
Usage in Section
// From src/services/config/builders.rs
serenity::CreateSection::new(
vec![
serenity::CreateSectionComponent::TextDisplay(
serenity::CreateTextDisplay::new(format!(
"**{}**\n{}",
module_name,
description
))
),
],
accessory
)
Common Patterns
Status Display
// From src/services/status.rs
serenity::CreateTextDisplay::new(format!(
"**Status**: {}\n\
**Uptime**: {}\n\
**Guilds**: {}",
if healthy { "✅ Online" } else { "❌ Offline" },
uptime_str,
guild_count
))
Field List
serenity::CreateTextDisplay::new(format!(
"**User**: <@{}>\n\
**Action**: {}\n\
**Reason**: {}\n\
**Timestamp**: <t:{}:F>",
user_id, action, reason, timestamp
))
Code Block
serenity::CreateTextDisplay::new(format!(
"```json\n{}\n```",
serde_json::to_string_pretty(&data)?
))
Thumbnail (Type 11)
What is Thumbnail?
Displays a small image, typically used as a Section accessory.
Status in Codebase: ❌ Not yet used (but supported by Serenity)
Creating Thumbnail
Basic Thumbnail
serenity::CreateThumbnail::new("https://example.com/image.png")
As Section Accessory
serenity::CreateSection::new(
vec![
serenity::CreateSectionComponent::TextDisplay(
serenity::CreateTextDisplay::new("**User Profile**")
),
],
serenity::CreateSectionAccessory::Thumbnail(
serenity::CreateThumbnail::new(user_avatar_url)
),
)
Potential Use Cases
User Profile
serenity::CreateSection::new(
vec![
serenity::CreateSectionComponent::TextDisplay(
serenity::CreateTextDisplay::new(format!(
"**{}**\nJoined: <t:{}:R>",
user.name, joined_timestamp
))
),
],
serenity::CreateSectionAccessory::Thumbnail(
serenity::CreateThumbnail::new(user.avatar_url())
),
)
Server Info
serenity::CreateSection::new(
vec![
serenity::CreateSectionComponent::TextDisplay(
serenity::CreateTextDisplay::new(format!(
"**{}**\nMembers: {}",
guild.name, guild.member_count
))
),
],
serenity::CreateSectionAccessory::Thumbnail(
serenity::CreateThumbnail::new(guild.icon_url())
),
)
Media Gallery (Type 12)
What is Media Gallery?
Displays multiple images or videos in a gallery layout.
Status in Codebase: ❌ Not yet used (but supported by Serenity)
Creating Media Gallery
Basic Gallery
serenity::CreateMediaGallery::new(vec![
serenity::CreateMediaGalleryItem::new("https://example.com/image1.png"),
serenity::CreateMediaGalleryItem::new("https://example.com/image2.png"),
])
Multiple Images
let items: Vec<_> = image_urls
.iter()
.map(|url| serenity::CreateMediaGalleryItem::new(url))
.collect();
serenity::CreateMediaGallery::new(items)
Usage in Container
components.push(serenity::CreateContainerComponent::MediaGallery(
serenity::CreateMediaGallery::new(vec![
serenity::CreateMediaGalleryItem::new("https://cdn.discordapp.com/attachments/.../image1.png"),
serenity::CreateMediaGalleryItem::new("https://cdn.discordapp.com/attachments/.../image2.png"),
])
));
Potential Use Cases
Violation Evidence
// Show screenshots of rule violations
let screenshots = vec![
"https://cdn.example.com/screenshot1.png",
"https://cdn.example.com/screenshot2.png",
];
let gallery_items: Vec<_> = screenshots
.iter()
.map(|url| serenity::CreateMediaGalleryItem::new(*url))
.collect();
components.push(serenity::CreateContainerComponent::MediaGallery(
serenity::CreateMediaGallery::new(gallery_items)
));
Audit Log Attachments
// Display before/after images in moderation logs
serenity::CreateMediaGallery::new(vec![
serenity::CreateMediaGalleryItem::new(before_image_url),
serenity::CreateMediaGalleryItem::new(after_image_url),
])
File (Type 13)
What is File?
Embeds a file attachment directly into the message body.
Status in Codebase: ❌ Not yet used (but supported by Serenity)
Creating File Component
Basic File
serenity::CreateFile::new(
"https://example.com/document.pdf",
"document.pdf"
)
With Custom Filename
serenity::CreateFile::new(
attachment_url,
format!("export_{}.json", timestamp)
)
Usage in Container
components.push(serenity::CreateContainerComponent::File(
serenity::CreateFile::new(
"https://cdn.discordapp.com/attachments/.../config.json",
"server_config.json"
)
));
Potential Use Cases
Configuration Export
// Export server configuration as JSON
let config_json = serde_json::to_string_pretty(&guild_config)?;
// Upload to Discord CDN first, then:
components.push(serenity::CreateContainerComponent::File(
serenity::CreateFile::new(uploaded_url, "config.json")
));
Log Export
// Attach audit log file
components.push(serenity::CreateContainerComponent::File(
serenity::CreateFile::new(
log_file_url,
format!("audit_log_{}.txt", guild_id)
)
));
Backup Files
// Provide downloadable backup
serenity::CreateFile::new(
backup_url,
format!("backup_{}_{}.zip", guild_id, timestamp)
)
Complete Content Example
pub fn build_user_profile(
user: &serenity::User,
joined_at: i64,
roles: &[String],
) -> Vec<serenity::CreateComponent> {
let mut inner_components = vec![];
// Header with user avatar (if Thumbnail was used)
inner_components.push(serenity::CreateContainerComponent::TextDisplay(
serenity::CreateTextDisplay::new(format!("## {}", user.name))
));
inner_components.push(serenity::CreateContainerComponent::Separator(
serenity::CreateSeparator::new(false)
));
// User info
inner_components.push(serenity::CreateContainerComponent::TextDisplay(
serenity::CreateTextDisplay::new(format!(
"**ID**: `{}`\n\
**Joined**: <t:{}:R>\n\
**Mention**: <@{}>",
user.id, joined_at, user.id
))
));
// Separator
inner_components.push(serenity::CreateContainerComponent::Separator(
serenity::CreateSeparator::new(true)
));
// Roles section
inner_components.push(serenity::CreateContainerComponent::TextDisplay(
serenity::CreateTextDisplay::new("**Roles**")
));
inner_components.push(serenity::CreateContainerComponent::TextDisplay(
serenity::CreateTextDisplay::new(roles.join(", "))
));
vec![serenity::CreateComponent::Container(
serenity::CreateContainer::new(inner_components)
.accent_color(0x5865F2)
)]
}
Markdown Reference
Text Display supports Discord Markdown:
**bold** *italic* __underline__ ~~strikethrough~~ `code` ```code block``` # Heading 1 ## Heading 2 ### Heading 3 [link](https://example.com) <@user_id> - User mention <@&role_id> - Role mention <#channel_id> - Channel mention <t:timestamp:F> - Timestamp (full date) <t:timestamp:R> - Timestamp (relative)
Best Practices
- •Use Text Display everywhere - Replaces content/embed description
- •Markdown for formatting - Bold headers, italic notes
- •Mention syntax - Use
<@user_id>not@username - •Timestamps - Use
<t:unix:format>for dates - •Code blocks - Triple backticks for formatted code/JSON
- •Multi-line for fields - Use
\nfor field-style layouts - •Thumbnail for avatars - Small images as section accessories
- •Media Gallery for multiple - Use gallery for 2+ images
- •File for downloads - Embed downloadable attachments
Codebase Examples
Text Display:
- •
src/services/config/mod.rs:58-59- Simple label - •
src/services/status.rs:55-56- Heading with Markdown - •
src/services/logger.rs:92-93- Multi-line content - •
src/services/config/builders.rs:63-64- Formatted field
Separators:
- •
src/services/config/mod.rs:73-74- Non-spacing - •
src/services/logger.rs:96-97- Spacing separator
Container Usage:
- •
src/services/logger.rs:120-122- Logger message - •
src/services/status.rs:166-168- Status display - •
src/services/help.rs:78-80- Help menu
Migration from Legacy Embeds
Old (Legacy Embed)
ctx.send(poise::CreateReply::default()
.embed(serenity::CreateEmbed::new()
.title("Title")
.description("Description")
.field("Field 1", "Value 1", false)
.color(0x5865F2)
)
).await?;
New (Components v2)
let mut components = vec![];
components.push(serenity::CreateContainerComponent::TextDisplay(
serenity::CreateTextDisplay::new("## Title")
));
components.push(serenity::CreateContainerComponent::TextDisplay(
serenity::CreateTextDisplay::new("Description")
));
components.push(serenity::CreateContainerComponent::Separator(
serenity::CreateSeparator::new(false)
));
components.push(serenity::CreateContainerComponent::TextDisplay(
serenity::CreateTextDisplay::new("**Field 1**\nValue 1")
));
ctx.send(poise::CreateReply::default()
.flags(serenity::MessageFlags::IS_COMPONENTS_V2)
.components(vec![serenity::CreateComponent::Container(
serenity::CreateContainer::new(components)
.accent_color(0x5865F2)
)])
).await?;
See Also
- •
components-v2-layout- Container, Section, Separator - •
components-v2-interactive- Buttons, Selects, Action Rows - •
components-v2-modals- Modals with Labels