Buttons

With the components API, you can create interactive message components. On this page, we'll cover how to send, receive, and respond to buttons using discord.js!

TIP

This page is a follow-up to the slash commands page. Please carefully read those first so that you can understand the methods used in this section.

Building and sending buttons

Buttons are one of the MessageComponent classes, which can be sent via messages or interaction responses. A button, as any other message component, must be in an ActionRow.

WARNING

You can have a maximum of five ActionRows per message, and five buttons within an ActionRow.

If you're using TypeScript you'll need to specify the type of components your action row holds. This can be done by specifying the component builder you will add to it using a generic parameter in ActionRowBuilderopen in new window.

- new ActionRowBuilder()
+ new ActionRowBuilder<ButtonBuilder>()
1
2

To create your buttons, use the ActionRowBuilderopen in new window and ButtonBuilderopen in new window classes. Then, pass the resulting row object to ChatInputCommandInteraction#reply()open in new window in the components array of InteractionReplyOptionsopen in new window:

const { ActionRowBuilder, ButtonBuilder, ButtonStyle, Events } = require('discord.js');

client.on(Events.InteractionCreate, async interaction => {
	if (!interaction.isChatInputCommand()) return;

	if (interaction.commandName === 'button') {
		const row = new ActionRowBuilder()
			.addComponents(
				new ButtonBuilder()
					.setCustomId('primary')
					.setLabel('Click me!')
					.setStyle(ButtonStyle.Primary),
			);

		await interaction.reply({ content: 'I think you should,', components: [row] });
	}
});
 





 
 
 
 
 
 
 

 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

TIP

The custom id is a developer-defined string of up to 100 characters. Use this field to ensure you can uniquely define all incoming interactions from your buttons!

Restart your bot and then send the command to a channel your bot has access to. If all goes well, you should see something like this:

User used /button
Guide Bot Bot 03/26/2023
I think you should,

You can also send message components within an ephemeral response or alongside message embeds.

const { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder, Events } = require('discord.js');

client.on(Events.InteractionCreate, async interaction => {
	if (!interaction.isChatInputCommand()) return;

	if (interaction.commandName === 'button') {
		const row = new ActionRowBuilder()
			.addComponents(
				// ...
			);

		const embed = new EmbedBuilder()
			.setColor(0x0099FF)
			.setTitle('Some title')
			.setURL('https://discord.js.org')
			.setDescription('Some description here');

		await interaction.reply({ content: 'I think you should,', ephemeral: true, embeds: [embed], components: [row] });
	}
});
 










 
 
 
 
 

 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
User used /button
Guide Bot Bot 03/26/2023
I think you should,
Some description here
Only you can see this

Disabled buttons

If you want to prevent a button from being used, but not remove it from the message, you can disable it with the ButtonBuilder#setDisabled()open in new window method:

const button = new ButtonBuilder()
	.setCustomId('primary')
	.setLabel('Click me!')
	.setStyle(ButtonStyle.Primary)
	.setDisabled(true);




 
1
2
3
4
5
User used /button
Guide Bot Bot 03/26/2023
I think you should,

Emoji buttons

If you want to use a guild emoji within a ButtonBuilderopen in new window, you can use the ButtonBuilder#setEmoji()open in new window method:

const button = new ButtonBuilder()
	.setCustomId('primary')
	.setLabel('Primary')
	.setStyle(ButtonStyle.Primary)
	.setEmoji('123456789012345678');




 
1
2
3
4
5

Now you know all there is to building and sending a Button! Let's move on to receiving button interactions!

Receiving button interactions

Component collectors

Message component interactions can be collected within the scope of the slash command that sent them by utilising an InteractionCollectoropen in new window, or their promisified awaitMessageComponent variant. These both provide instances of the MessageComponentInteractionopen in new window class as collected items.

TIP

You can create the collectors on either a message or a channel.

For a detailed guide on receiving message components via collectors, please refer to the collectors guide.

The interactionCreate event

To receive a ButtonInteractionopen in new window event, attach an Client#event:interactionCreateopen in new window event listener to your client and use the BaseInteraction#isButton()open in new window type guard to make sure you only receive buttons:

client.on(Events.InteractionCreate, interaction => {
	if (!interaction.isButton()) return;
	console.log(interaction);
});

 


1
2
3
4

Responding to buttons

The MessageComponentInteractionopen in new window class provides the same methods as the ChatInputCommandInteractionopen in new window class. These methods behave equally:

  • reply()
  • editReply()
  • deferReply()
  • fetchReply()
  • deleteReply()
  • followUp()

Updating the button message

The MessageComponentInteractionopen in new window class also provides an MessageComponentInteraction#update()open in new window method to update the message the button is attached to. Passing an empty array to the components option will remove any buttons after one has been clicked.

This method should be used in favour of editReply() on the original interaction, to ensure you respond to the button interaction.

const filter = i => i.customId === 'primary' && i.user.id === '122157285790187530';

const collector = interaction.channel.createMessageComponentCollector({ filter, time: 15000 });

collector.on('collect', async i => {
	await i.update({ content: 'A button was clicked!', components: [] });
});

collector.on('end', collected => console.log(`Collected ${collected.size} items`));





 



1
2
3
4
5
6
7
8
9

Deferring and updating the button message

In addition to deferring an interaction response, you can defer the button update, which will trigger a loading state and then revert to its original state:

const wait = require('node:timers/promises').setTimeout;

// ...

collector.on('collect', async i => {
	if (i.customId === 'primary') {
		await i.deferUpdate();
		await wait(4000);
		await i.editReply({ content: 'A button was clicked!', components: [] });
	}
});

collector.on('end', collected => console.log(`Collected ${collected.size} items`));
 





 
 
 




1
2
3
4
5
6
7
8
9
10
11
12
13

Button styles

Currently there are five different button styles available:

  • Primary, a blurple button;
  • Secondary, a grey button;
  • Success, a green button;
  • Danger, a red button;
  • Link, a button that navigates to a URL.
Guide Bot Bot 03/26/2023
Link

WARNING

Only Link buttons can have a url. Link buttons cannot have a customId and do not send an interaction event when clicked.