Buttons
const { ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js');
const row = new ActionRowBuilder().addComponents(
new ButtonBuilder().setCustomId('approve').setLabel('Approve').setStyle(ButtonStyle.Success),
new ButtonBuilder().setCustomId('reject').setLabel('Reject').setStyle(ButtonStyle.Danger)
);
await interaction.reply({ content: 'Choose:', components: [row] });
Select Menus
const { StringSelectMenuBuilder, ActionRowBuilder } = require('discord.js');
const select = new StringSelectMenuBuilder()
.setCustomId('picker')
.setPlaceholder('Pick an option')
.addOptions(
{ label: 'A', value: 'a' },
{ label: 'B', value: 'b' }
);
await interaction.reply({ components: [new ActionRowBuilder().addComponents(select)] });
Modals
const { ModalBuilder, TextInputBuilder, TextInputStyle, ActionRowBuilder } = require('discord.js');
const modal = new ModalBuilder().setCustomId('feedback').setTitle('Feedback');
const input = new TextInputBuilder().setCustomId('message').setLabel('Your message').setStyle(TextInputStyle.Paragraph);
modal.addComponents(new ActionRowBuilder().addComponents(input));
await interaction.showModal(modal);
Best Practices
- Namespace
customId
values (e.g.,order:approve:123
) - Expire components after use; handle stale interactions
- Use ephemeral replies for sensitive actions
Timing Rules and Deferrals
You must acknowledge an interaction within 3 seconds by replying or deferring; after that, the token expires. Once acknowledged, you have up to 15 minutes to send or edit the response and follow‑ups. For operations that may exceed 3 seconds (API calls, DB work), defer immediately, then edit the reply when ready.
// discord.js (v14+)
client.on('interactionCreate', async (interaction) => {
if (!interaction.isChatInputCommand()) return;
try {
await interaction.deferReply({ ephemeral: true }); // within 3s
const data = await slowOperation();
await interaction.editReply({ content: `Done: ${data}` }); // within 15 min
} catch (err) {
if (interaction.deferred || interaction.replied) {
await interaction.followUp({ content: 'Error occurred.', ephemeral: true });
} else {
await interaction.reply({ content: 'Error occurred.', ephemeral: true });
}
}
});