Replying to slash commands

Discord provides developers the option to create client-integrated slash commands. In this section, we'll cover how to respond to these commands using discord.js!

TIP

You need at least one slash command registered on your application to continue with the instructions on this page. If you haven't done that yet, refer to the previous page.

Receiving interactions

Every slash command is an interaction, so to respond to a command, you need to set up an event listener that will execute code when your application receives an interaction:

client.on('interactionCreate', interaction => {
	console.log(interaction);
});
1
2
3

However, not every interaction is a slash command (e.g. MessageComponents). Make sure to only receive slash commands by making use of the CommandInteraction#isCommand() method:

client.on('interactionCreate', interaction => {
	if (!interaction.isCommand()) return;
	console.log(interaction);
});

 


1
2
3
4

Responding to a command

There are multiple ways of responding to a slash command, each of these are covered in the following segments. The most common way of sending a response is by using the CommandInteraction#reply() method:

WARNING

Initially an interaction token is only valid for three seconds, so that's the timeframe in which you are able to use the CommandInteraction#reply() method. Responses that require more time ("Deferred Responses") are explained later in this page.

client.on('interactionCreate', async interaction => {
	if (!interaction.isCommand()) return;

	if (interaction.commandName === 'ping') {
		await interaction.reply('Pong!');
	}
});
 


 
 
 

1
2
3
4
5
6
7

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 /ping
Guide Bot Bot 06/06/2023
Pong!

You've successfully sent a response to a slash command! This is only the beginning, there's more to look out for so let's move on to further ways of replying to a command!

Ephemeral responses

You may not always want everyone who has access to the channel to see a slash command's response. Thankfully, Discord implemented a way to hide messages from everyone but the executor of the slash command. This type of message is called ephemeral and can be set by using ephemeral: true in the InteractionReplyOptions, as follows:

client.on('interactionCreate', async interaction => {
	if (!interaction.isCommand()) return;

	if (interaction.commandName === 'ping') {
		await interaction.reply({ content: 'Pong!', ephemeral: true });
	}
});




 


1
2
3
4
5
6
7

Now when you run your command again, you should see something like this:

User used /ping
Guide Bot Bot 06/06/2023
Pong!
Only you can see this

Editing responses

After you've sent an initial response, you may want to edit that response for various reasons. This can be achieved with the CommandInteraction#editReply() method:

WARNING

After the initial response, an interaction token is valid for 15 minutes, so this is the timeframe in which you can edit the response and send follow-up messages.

const wait = require('util').promisify(setTimeout);

client.on('interactionCreate', async interaction => {
	if (!interaction.isCommand()) return;

	if (interaction.commandName === 'ping') {
		await interaction.reply('Pong!');
		await wait(2000);
		await interaction.editReply('Pong again!');
	}
});
 






 
 


1
2
3
4
5
6
7
8
9
10
11

Deferred responses

As previously mentioned, you have three seconds to respond to an interaction before its token becomes invalid. But what if you have a command that performs a task which takes longer than three seconds before being able to reply?

In this case, you can make use of the CommandInteraction#deferReply() method, which triggers the <application> is thinking... message and also acts as initial response. This allows you 15 minutes to complete your tasks before responding.

const wait = require('util').promisify(setTimeout);

client.on('interactionCreate', async interaction => {
	if (!interaction.isCommand()) return;

	if (interaction.commandName === 'ping') {
		await interaction.deferReply();
		await wait(4000);
		await interaction.editReply('Pong!');
	}
});






 
 
 


1
2
3
4
5
6
7
8
9
10
11

If you have a command that performs longer tasks, be sure to call deferReply() as early as possible.

You can also pass an ephemeral flag to the InteractionDeferOptions:

await interaction.deferReply({ ephemeral: true });
1

Follow-ups

Replying to slash commands is great and all, but what if you want to send multiple responses instead of just one? Follow-up messages got you covered, you can use CommandInteraction#followUp() to send multiple responses:

WARNING

After the initial response, an interaction token is valid for 15 minutes, so this is the timeframe in which you can edit the response and send follow-up messages.

client.on('interactionCreate', async interaction => {
	if (!interaction.isCommand()) return;

	if (interaction.commandName === 'ping') {
		await interaction.reply('Pong!');
		await interaction.followUp('Pong again!');
	}
});





 


1
2
3
4
5
6
7
8

If you run this code you should end up having something that looks like this:

User used /ping
Guide Bot Bot 06/06/2023
Pong!
Guide Bot Bot Pong!
Guide Bot Bot 06/06/2023
Pong again!

You can also pass an ephemeral flag to the InteractionReplyOptions:

await interaction.followUp({ content: 'Pong again!', ephemeral: true });
1
User used /ping
Guide Bot Bot 06/06/2023
Pong!
Guide Bot Bot Pong!
Guide Bot Bot 06/06/2023
Pong again!
Only you can see this

That's all, now you know everything there is to know on how to reply to slash commands!

TIP

Interaction responses can use masked links (e.g. [text](http://site.com)) and global emojis in the message content.

Parsing options

Command options

In this section, we'll cover how to access the values of a command's options. Let's assume you have a command that contains the following options:

const { SlashCommandBuilder } = require('@discordjs/builders');

const data = new SlashCommandBuilder()
	.setName('ping')
	.setDescription('Replies with Pong!')
	.addStringOption(option => option.setName('input').setDescription('Enter a string'))
	.addIntegerOption(option => option.setName('int').setDescription('Enter an integer'))
	.addNumberOption(option => option.setName('num').setDescription('Enter a number'))
	.addBooleanOption(option => option.setName('choice').setDescription('Select a boolean'))
	.addUserOption(option => option.setName('target').setDescription('Select a user'))
	.addChannelOption(option => option.setName('destination').setDescription('Select a channel'))
	.addRoleOption(option => option.setName('muted').setDescription('Select a role'))
	.addMentionableOption(option => option.setName('mentionable').setDescription('Mention something'));





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

You can get() these options from the CommandInteractionOptionResolver as shown below:

const string = interaction.options.getString('input');
const integer = interaction.options.getInteger('int');
const number = interaction.options.getNumber('num');
const boolean = interaction.options.getBoolean('choice');
const user = interaction.options.getUser('target');
const member = interaction.options.getMember('target');
const channel = interaction.options.getChannel('destination');
const role = interaction.options.getRole('muted');
const mentionable = interaction.options.getMentionable('mentionable');

console.log([string, integer, boolean, user, member, channel, role, mentionable]);
1
2
3
4
5
6
7
8
9
10
11

TIP

If you want the Snowflake of a structure instead, grab the option via get() and access the Snowflake via the value property. Note that you should use const { value: name } = ... here to destructure and renameopen in new window the value obtained from the CommandInteractionOptionopen in new window structure to avoid identifier name conflicts.

Subcommands

If you have a command that contains subcommands, you can parse them in a very similar way as to the above examples. Let's say your command looks like this:

const { SlashCommandBuilder } = require('@discordjs/builders');

const data = new SlashCommandBuilder()
	.setName('info')
	.setDescription('Get info about a user or a server!')
	.addSubcommand(subcommand =>
		subcommand
			.setName('user')
			.setDescription('Info about a user')
			.addUserOption(option => option.setName('target').setDescription('The user')))
	.addSubcommand(subcommand =>
		subcommand
			.setName('server')
			.setDescription('Info about the server'));





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

The following snippet details the logic needed to parse the subcommands and respond accordingly using the CommandInteractionOptionResolver#getSubcommand() method:

client.on('interactionCreate', async interaction => {
	if (!interaction.isCommand()) return;

	if (interaction.commandName === 'info') {
		if (interaction.options.getSubcommand() === 'user') {
			const user = interaction.options.getUser('target');

			if (user) {
				await interaction.reply(`Username: ${user.username}\nID: ${user.id}`);
			} else {
				await interaction.reply(`Your username: ${interaction.user.username}\nYour ID: ${interaction.user.id}`);
			}
		} else if (interaction.options.getSubcommand() === 'server') {
			await interaction.reply(`Server name: ${interaction.guild.name}\nTotal members: ${interaction.guild.memberCount}`);
		}
	}
});




 
 
 
 
 
 
 
 
 
 
 


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

Fetching and deleting responses

DANGER

You cannot fetch nor delete an ephemeral message.

In addition to replying to a slash command, you may also want to delete the initial reply. You can use CommandInteraction#deleteReply() for this:

await interaction.reply('Pong!');
await interaction.deleteReply();

 
1
2

Lastly, you may require the Message object of a reply for various reasons, such as adding reactions. You can use the CommandInteraction#fetchReply() method to fetch the Message instance of an initial response:

await interaction.reply('Pong!');
const message = await interaction.fetchReply();
console.log(message);
1
2
3