Find us on social media
Slash CommandsInteractionsdiscord.js6 min read
Configure Slash Commands
Register slash commands globally and per-guild, handle interactions, options, subcommands and autocomplete.
What are Slash Commands?
Slash commands are the modern way to interact with Discord bots. They appear when users type / and offer autocomplete, type validation and better UX.
Step 1: Register commands
Create a deploy-commands.js file:
javascript
const { REST, Routes, SlashCommandBuilder } = require('discord.js');
require('dotenv').config();
const commands = [
new SlashCommandBuilder()
.setName('ping')
.setDescription('Replies with pong and latency'),
new SlashCommandBuilder()
.setName('info')
.setDescription('Shows user info')
.addUserOption(option =>
option.setName('user')
.setDescription('The user to look up')
.setRequired(true)
),
new SlashCommandBuilder()
.setName('config')
.setDescription('Server configuration')
.addSubcommand(sub =>
sub.setName('prefix')
.setDescription('Change the prefix')
.addStringOption(opt =>
opt.setName('new').setDescription('New prefix').setRequired(true)
)
)
];
const rest = new REST().setToken(process.env.DISCORD_TOKEN);
(async () => {
// Global registration (takes ~1 hour to propagate)
await rest.put(
Routes.applicationCommands(process.env.CLIENT_ID),
{ body: commands.map(c => c.toJSON()) }
);
console.log('Commands registered globally');
// Per-guild registration (instant, ideal for development)
await rest.put(
Routes.applicationGuildCommands(process.env.CLIENT_ID, process.env.GUILD_ID),
{ body: commands.map(c => c.toJSON()) }
);
console.log('Commands registered in dev guild');
})();Step 2: Handle interactions
javascript
client.on('interactionCreate', async interaction => {
if (!interaction.isChatInputCommand()) return;
const { commandName } = interaction;
if (commandName === 'ping') {
const latency = Date.now() - interaction.createdTimestamp;
await interaction.reply(`Pong! Latency: ${latency}ms`);
}
if (commandName === 'info') {
const user = interaction.options.getUser('user');
await interaction.reply(`User: ${user.tag}\nID: ${user.id}`);
}
});Step 3: Advanced options
Available option types:
javascript
// String with predefined choices
.addStringOption(opt =>
opt.setName('color')
.setDescription('Pick a color')
.addChoices(
{ name: 'Red', value: 'red' },
{ name: 'Blue', value: 'blue' }
)
)
// Integer with range
.addIntegerOption(opt =>
opt.setName('amount')
.setDescription('Amount')
.setMinValue(1)
.setMaxValue(100)
)Step 4: Autocomplete
javascript
// In the builder
.addStringOption(opt =>
opt.setName('game')
.setDescription('Game name')
.setAutocomplete(true)
)
// In the handler
client.on('interactionCreate', async interaction => {
if (interaction.isAutocomplete()) {
const focused = interaction.options.getFocused();
const choices = ['Minecraft', 'FiveM', 'CS2', 'Valorant'];
const filtered = choices.filter(c =>
c.toLowerCase().startsWith(focused.toLowerCase())
);
await interaction.respond(
filtered.map(c => ({ name: c, value: c }))
);
}
});Recommendations
- Use per-guild registration during development (it's instant)
- Register globally only when the command is production-ready
- Implement error handling in every command
- Use
interaction.deferReply()if the command takes more than 3 seconds
Was this guide helpful?