Message components are interactive elements that can be added to Discord messages, including buttons, text inputs, and action rows for organizing components.
Container for organizing message components in rows. Generic type parameter specifies which component types it can contain.
class ActionRowBuilder<T extends AnyComponentBuilder> {
readonly components: T[];
constructor(data?: Partial<APIActionRowComponent<APIComponentInActionRow>>);
addComponents(...components: RestOrArray<T>): this;
setComponents(...components: RestOrArray<T>): this;
spliceComponents(index: number, deleteCount: number, ...components: RestOrArray<T>): this;
toJSON(): APIActionRowComponent<APIComponentInMessageActionRow | APIComponentInModalActionRow>;
}Interactive button component with various styles and actions.
class ButtonBuilder {
readonly data: Partial<APIButtonComponent>;
constructor(data?: APIButtonComponent);
setStyle(style: ButtonStyle): this;
setURL(url: string): this;
setCustomId(customId: string): this;
setEmoji(emoji: APIMessageComponentEmoji): this;
setDisabled(disabled?: boolean): this;
setLabel(label: string): this;
setSKUId(skuId: Snowflake): this;
toJSON(): APIButtonComponent;
}Text input component for use in modals.
class TextInputBuilder {
readonly data: Partial<APITextInputComponent>;
constructor(data?: APITextInputComponent);
setCustomId(customId: string): this;
setLabel(label: string): this;
setStyle(style: TextInputStyle): this;
setMinLength(minLength: number): this;
setMaxLength(maxLength: number): this;
setPlaceholder(placeholder: string): this;
setValue(value: string): this;
setRequired(required?: boolean): this;
toJSON(): APITextInputComponent;
}Abstract base class for all component builders.
abstract class ComponentBuilder<ComponentDataType> {
readonly data: Partial<ComponentDataType>;
constructor(data?: Partial<ComponentDataType>);
toJSON(): ComponentDataType;
}enum ButtonStyle {
Primary = 1,
Secondary = 2,
Success = 3,
Danger = 4,
Link = 5,
Premium = 6
}enum TextInputStyle {
Short = 1,
Paragraph = 2
}type MessageActionRowComponentBuilder =
| ButtonBuilder
| ChannelSelectMenuBuilder
| MentionableSelectMenuBuilder
| RoleSelectMenuBuilder
| StringSelectMenuBuilder
| UserSelectMenuBuilder;
type ModalActionRowComponentBuilder = TextInputBuilder;
type AnyComponentBuilder = MessageActionRowComponentBuilder | ModalActionRowComponentBuilder;
type MessageComponentBuilder =
| ActionRowBuilder<MessageActionRowComponentBuilder>
| MessageActionRowComponentBuilder;
type ModalComponentBuilder =
| ActionRowBuilder<ModalActionRowComponentBuilder>
| ModalActionRowComponentBuilder;interface APIMessageComponentEmoji {
id?: Snowflake | null;
name?: string | null;
animated?: boolean;
}import { ButtonBuilder, ActionRowBuilder, ButtonStyle } from "@discordjs/builders";
// Primary button with custom ID
const primaryButton = new ButtonBuilder()
.setCustomId('primary_button')
.setLabel('Click Me!')
.setStyle(ButtonStyle.Primary);
// Link button
const linkButton = new ButtonBuilder()
.setURL('https://discord.js.org')
.setLabel('Visit Website')
.setStyle(ButtonStyle.Link);
// Button with emoji
const emojiButton = new ButtonBuilder()
.setCustomId('emoji_button')
.setLabel('React!')
.setStyle(ButtonStyle.Secondary)
.setEmoji({
name: '๐',
id: null,
animated: false
});
// Disabled button
const disabledButton = new ButtonBuilder()
.setCustomId('disabled_button')
.setLabel('Disabled')
.setStyle(ButtonStyle.Danger)
.setDisabled(true);const row = new ActionRowBuilder<ButtonBuilder>()
.addComponents(
new ButtonBuilder()
.setCustomId('accept')
.setLabel('Accept')
.setStyle(ButtonStyle.Success),
new ButtonBuilder()
.setCustomId('decline')
.setLabel('Decline')
.setStyle(ButtonStyle.Danger),
new ButtonBuilder()
.setURL('https://discord.js.org/guide')
.setLabel('Learn More')
.setStyle(ButtonStyle.Link)
);import { TextInputBuilder, TextInputStyle } from "@discordjs/builders";
// Short text input
const nameInput = new TextInputBuilder()
.setCustomId('user_name')
.setLabel('What is your name?')
.setStyle(TextInputStyle.Short)
.setMinLength(2)
.setMaxLength(50)
.setPlaceholder('Enter your name here')
.setRequired(true);
// Paragraph text input
const feedbackInput = new TextInputBuilder()
.setCustomId('feedback')
.setLabel('Tell us about your experience')
.setStyle(TextInputStyle.Paragraph)
.setMinLength(10)
.setMaxLength(1000)
.setPlaceholder('Share your thoughts...')
.setValue('Default feedback text')
.setRequired(false);const textInputRow = new ActionRowBuilder<TextInputBuilder>()
.addComponents(
new TextInputBuilder()
.setCustomId('reason')
.setLabel('Reason for report')
.setStyle(TextInputStyle.Paragraph)
.setRequired(true)
);const row = new ActionRowBuilder<ButtonBuilder>();
// Add components individually
row.addComponents(
new ButtonBuilder()
.setCustomId('button1')
.setLabel('Button 1')
.setStyle(ButtonStyle.Primary)
);
// Add multiple components
row.addComponents(
new ButtonBuilder()
.setCustomId('button2')
.setLabel('Button 2')
.setStyle(ButtonStyle.Secondary),
new ButtonBuilder()
.setCustomId('button3')
.setLabel('Button 3')
.setStyle(ButtonStyle.Success)
);
// Replace component at index 1
row.spliceComponents(1, 1,
new ButtonBuilder()
.setCustomId('new_button')
.setLabel('Replaced Button')
.setStyle(ButtonStyle.Danger)
);
// Set all components at once
row.setComponents(
new ButtonBuilder()
.setCustomId('only_button')
.setLabel('Only Button')
.setStyle(ButtonStyle.Primary)
);Creates appropriate component builder from API data.
function createComponentBuilder(data: APIMessageComponent | APIModalComponent): AnyComponentBuilder;Usage:
import { createComponentBuilder } from "@discordjs/builders";
const apiButtonData = {
type: 2, // Button
style: 1, // Primary
label: 'Click me!',
custom_id: 'my_button'
};
const button = createComponentBuilder(apiButtonData);Discord enforces these limits on message components: