Message Components
Message components are new components you can add to messages, such as buttons and select menus. There are currently four different types of message components:
ActionRow
Represents a row of buttons on a message. You can add up to 5 buttons to the row, which can then be added to the message. You can only add buttons to action rows.
$row = ActionRow::new()
->addComponent(Button::new(Button::STYLE_SUCCESS));
Functions
| name | description |
|---|---|
addComponent($component)
|
adds a component to action row. must be a button component. |
removeComponent($component)
|
removes the given component from the action row. |
getComponents(): Component[]
|
returns all the action row components in an array. |
Button
Represents a button attached to a message. You cannot directly attach a button to a message, it must be contained inside an ActionRow
.
$button = Button::new(Button::STYLE_SUCCESS)
->setLabel('push me!');
There are 5 different button styles:
| name | constant | colour |
|---|---|---|
| primary | Button::STYLE_PRIMARY
|
blurple |
| secondary | Button::STYLE_SECONDARY
|
grey |
| success | Button::STYLE_SUCCESS
|
green |
| danger | Button::STYLE_DANGER
|
red |
| link | Button::STYLE_LINK
|
grey |
Functions
| name | description |
|---|---|
setStyle($style)
|
sets the style of the button. must be one of the above constants. |
setLabel($label)
|
sets the label of the button. maximum 80 characters. |
setEmoji($emoji)
|
sets the emoji for the button. must be an Emoji
object. |
setCustomId($custom_id)
|
sets the custom ID of the button. maximum 100 characters. will be automatically generated if left null. not applicable for link buttons. |
setUrl($url)
|
sets the url of the button. only for buttons with the Button::STYLE_LINK
style. |
setDisabled($disabled)
|
sets whether the button is disabled or not. |
setListener($listener, $discord)
|
sets the listener for the button. see below for more information. not applicable for link buttons. |
removeListener()
|
removes the listener from the button. |
Adding a button listener
If you add a button you probably want to listen for when it is clicked. This is done through the setListener(callable $listener, Discord $discord)
function.
The callable $listener
will be a function which is called with the Interaction
object that triggered the button press. You must also pass the function the $discord
client.
$button->setListener(function (Interaction $interaction) {
$interaction->respondWithMessage(MessageBuilder::new()
->setContent('why\'d u push me?'));
}, $discord);
If the interaction is not responded to after the function is called, the interaction will be automatically acknowledged with no response. If you are going to acknowledge the interaction after a delay (e.g. HTTP request, arbitrary timeout) you should return a promise from the listener to prevent the automatic acknowledgement:
$button->setListener(function (Interaction $interaction) use ($discord) {
return someFunctionWhichWillReturnAPromise()->then(function ($returnValueFromFunction) use ($interaction) {
$interaction->respondWithMessage(MessageBuilder::new()
->setContent($returnValueFromFunction));
});
}, $discord);
TextInput
Text inputs are an interactive component that render on modals.
$textInput = TextInput::new('Label', TextInput::TYPE_SHORT, 'custom id')
->setRequired(true);
They can be used to collect short-form or long-form text:
| style | constant |
|---|---|
| Short (single line) | TextInput::STYLE_SHORT
|
| Paragraph (multi line) | TextInput::STYLE_PARAGRAPH
|
Functions
| name | description |
|---|---|
setCustomId($custom_id)
|
sets the custom ID of the text input. maximum 100 characters. will be automatically generated if left null. |
setStyle($style)
|
sets the style of the text input. must be one of the above constants. |
setLabel($label)
|
sets the label of the button. maximum 80 characters. |
setMinLength($min_length)
|
the minimum length of value. between 0 and 4000, default 0. |
setMaxLength($max_length)
|
the maximum length of value. between 1 and 4000, default 4000. |
setValue($value)
|
sets a pre-filled value for the text input. maximum 4000 characters. |
setPlaceholder($placeholder)
|
sets a placeholder string to be displayed when text input is empty. max 100 characters. |
setRequired($required)
|
sets whether the text input is required or not. |
Website component embeds
Discord can render website link previews as display-only components. These appear
in $message->embeds
as EmbedComponents
with a components
collection
containing one typed Container
. The Container's children and Section accessories
use the existing component Parts. They are separate from $message->components
.
REST responses, gateway message create/update events and forwarded snapshots use
the same embed type map. Missing or null embed components yield an empty collection.
use Discord\Parts\Channel\Message\Embed\EmbedComponents;
foreach ($message->embeds as $embed) {
if ($embed instanceof EmbedComponents) {
$container = $embed->components->first();
foreach ($container->components as $component) {
// Read the received display component, without sending an interaction.
}
}
}
Author website JSON with ComponentEmbedBuilder
, without constructing a Discord
client. This helper only builds a website preview; it does not connect or send a
message. DiscordPHP's bot runtime still runs as a long-lived CLI process.
use Discord\Builders\ComponentEmbedBuilder;
use Discord\Builders\Components\Container;
use Discord\Builders\Components\TextDisplay;
$preview = ComponentEmbedBuilder::new(
Container::new()
->setAccentColor(0x5865F2)
->addComponent(TextDisplay::new("# DiscordPHP\nAn asynchronous PHP library for Discord."))
);
$script = $preview->toScript();
// Include $script in the website's server-rendered <head>.
// toJson() can instead produce a linked JSON file.
The JSON has the shape {"component":{"type":17,"components":[...]}}
.
The helper snapshots the input, validates placement and supported display types,
and limits the escaped JSON to 3,000 UTF-8 bytes. Escapes count at their full length.
There can be at most 40 components including the root and Section accessories,
and 10 Media Gallery items across the whole preview. Thumbnails do not count as
gallery items. Nested Containers, File/select/input components, interactive or
premium buttons, and unexpected fields are rejected. Buttons must use style 5
with an HTTP(S) URL of at most 512 characters and a label, an emoji, or both;
custom_id
is forbidden.
toScript()
escapes angle brackets, including closing script tags, without
HTML-entity-encoding the JSON. Insert its output directly as the complete script
element; do not interpolate raw content into a script or inject it client-side.
fromPart()
deliberately rejects received Parts: fetched media metadata is not
website authoring input. Received component embeds are also rejected by
MessageBuilder
; the bot API only accepts authored rich embeds. To edit a bot
message containing a received preview, author the update with a fresh
MessageBuilder
rather than copying that preview with fromPart()
.
Keep Open Graph and Twitter fallback tags alongside the script. For example, the existing DiscordPHP community banner linked in README is PNG media:
<meta property="og:title" content="DiscordPHP">
<meta property="og:description" content="An asynchronous PHP library for Discord.">
<meta property="og:url" content="https://discordphp.org/">
<meta property="og:type" content="website">
<meta property="og:image" content="https://discord.com/api/guilds/115233111977099271/widget.png?style=banner1">
<meta name="twitter:card" content="summary_large_image">
<!-- Insert the complete $preview->toScript() output here. -->
For your own site, reuse its existing PNG, GIF, JPEG, WebP or AVIF brand assets. SVG previews are unsupported. Media URLs must be reachable HTTP(S) URLs, not attachments. Media URLs allow up to 2,048 characters. Galleries also support MP4, WebM and MOV video; Thumbnails require images. The helper checks URL syntax and supported file extensions, allowing extensionless URLs for separately verified media responses. A suffix does not prove the fetched media format. It does not fetch media or verify MIME types, crawler access or the ten-second fetch budget. Serve the tags in the initial HTML response and retain existing fallback metadata. The optional linked JSON URL must share the page's host, parent domain or subdomain.
See Discord's component embed contract and link preview metadata.