Setting Up Discord Slash Commands πͺ
16/03/2025 Development 5 mins read
Prerequisites
- An existing Discord bot project using Discord.js v14+
- Node.js v16.9.0 or higher
- Basic understanding of Discord.js
Understanding Slash Commands
Slash commands allow users to interact with your bot using /command syntax directly in Discordβs interface. They provide:
- Auto-completion of command names and options
- Standardized command structure
- Better permission handling
- Improved user experience
Project Setup
First, ensure you have the required dependencies:
Creating Command Files
Create a commands folder in your project and add individual command files:
my-discord-bot/βββ commands/β βββ ping.jsβ βββ info.jsβ βββ help.jsβββ index.jsβββ deploy-commands.jsExample Command File
const { SlashCommandBuilder } = require('discord.js');
module.exports = { data: new SlashCommandBuilder() .setName('ping') .setDescription('Replies with Pong!'), async execute(interaction) { await interaction.reply('Pong!'); },};Creating a Command with Options
const { SlashCommandBuilder } = require('discord.js');
module.exports = { data: new SlashCommandBuilder() .setName('echo') .setDescription('Echoes your input') .addStringOption(option => option.setName('message') .setDescription('The message to echo') .setRequired(true)) .addBooleanOption(option => option.setName('ephemeral') .setDescription('Whether the echo should be ephemeral')), async execute(interaction) { const message = interaction.options.getString('message'); const ephemeral = interaction.options.getBoolean('ephemeral') ?? false;
await interaction.reply({ content: message, ephemeral: ephemeral }); },};Deploying Commands
Create a deployment script to register your commands with Discord:
const { REST, Routes } = require('discord.js');const fs = require('node:fs');const path = require('node:path');require('dotenv').config();
const commands = [];const commandsPath = path.join(__dirname, 'commands');const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
for (const file of commandFiles) { const filePath = path.join(commandsPath, file); const command = require(filePath); commands.push(command.data.toJSON());}
const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN);
(async () => { try { console.log(`Started refreshing ${commands.length} application (/) commands.`);
// The put method is used to fully refresh all commands const data = await rest.put( Routes.applicationCommands(process.env.CLIENT_ID), { body: commands }, );
console.log(`Successfully reloaded ${data.length} application (/) commands.`); } catch (error) { console.error(error); }})();Update your .env file to include:
# .envDISCORD_TOKEN=your_token_hereCLIENT_ID=your_application_id_hereRun the deployment script:
node deploy-commands.jsHandling Commands in Your Main Bot File
Update your main bot file to handle the slash commands:
const fs = require('node:fs');const path = require('node:path');const { Client, Collection, Events, GatewayIntentBits } = require('discord.js');require('dotenv').config();
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
// Create a collection for commandsclient.commands = new Collection();const commandsPath = path.join(__dirname, 'commands');const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
for (const file of commandFiles) { const filePath = path.join(commandsPath, file); const command = require(filePath);
if ('data' in command && 'execute' in command) { client.commands.set(command.data.name, command); } else { console.log(`[WARNING] The command at ${filePath} is missing required "data" or "execute" property.`); }}
// When the client is ready, run this codeclient.once(Events.ClientReady, () => { console.log('Ready!');});
// Listen for interactions (slash commands)client.on(Events.InteractionCreate, async interaction => { if (!interaction.isChatInputCommand()) return;
const command = client.commands.get(interaction.commandName);
if (!command) return;
try { await command.execute(interaction); } catch (error) { console.error(error); await interaction.reply({ content: 'There was an error executing this command!', ephemeral: true }); }});
client.login(process.env.DISCORD_TOKEN);Advanced Command Options
Discord.js supports various command option types:
- String:
.addStringOption() - Integer:
.addIntegerOption() - Boolean:
.addBooleanOption() - User:
.addUserOption() - Channel:
.addChannelOption() - Role:
.addRoleOption() - Mentionable:
.addMentionableOption() - Number:
.addNumberOption() - Attachment:
.addAttachmentOption()
Example with Multiple Option Types
const { SlashCommandBuilder } = require('discord.js');
module.exports = { data: new SlashCommandBuilder() .setName('profile') .setDescription('Get profile information') .addUserOption(option => option.setName('target') .setDescription('The user to get info about') .setRequired(false)) .addStringOption(option => option.setName('format') .setDescription('Output format') .setRequired(false) .addChoices( { name: 'Simple', value: 'simple' }, { name: 'Detailed', value: 'detailed' }, { name: 'Full', value: 'full' }, )), async execute(interaction) { const target = interaction.options.getUser('target') ?? interaction.user; const format = interaction.options.getString('format') ?? 'simple';
// Implementation details... await interaction.reply(`Showing ${format} profile for ${target.username}`); },};Subcommands
You can organize related commands using subcommands:
const { SlashCommandBuilder } = require('discord.js');
module.exports = { data: new SlashCommandBuilder() .setName('settings') .setDescription('Manage bot settings') .addSubcommand(subcommand => subcommand .setName('view') .setDescription('View current settings')) .addSubcommand(subcommand => subcommand .setName('prefix') .setDescription('Change the bot prefix') .addStringOption(option => option.setName('character') .setDescription('The new prefix character') .setRequired(true))), async execute(interaction) { if (interaction.options.getSubcommand() === 'view') { await interaction.reply('Current settings: ...'); } else if (interaction.options.getSubcommand() === 'prefix') { const newPrefix = interaction.options.getString('character'); await interaction.reply(`Prefix updated to: ${newPrefix}`); } },};