Discord Slash Commands: Complete Implementation Guide

Published on

How to implement Discord slash commands in your bot. Covers registration, options, autocomplete, subcommands, permissions, and best practices for 2025.

Written by Jochem, Infrastructure Expert, 5-10 years experience in game server hosting, VPS infrastructure, and 24/7 streaming solutions. Read author bio →

Slash commands replaced prefix commands as the standard interaction method. If your bot still relies on !command, it's time to modernize.

Why Slash Commands?

  • Discoverability: Users type / and see all available commands
  • Validation: Discord validates input types before your bot receives them
  • Permissions: Server admins control command access through Discord's UI
  • Mobile-friendly: Works properly on mobile Discord
  • Required: Message content intent is now privileged - prefix commands need special approval

Registration

Global Commands

Available in all servers (up to 1 hour to propagate):

const { REST, Routes, SlashCommandBuilder } = require('discord.js');

const commands = [
    new SlashCommandBuilder()
        .setName('ping')
        .setDescription('Check bot latency'),
    new SlashCommandBuilder()
        .setName('userinfo')
        .setDescription('Get user information')
        .addUserOption(option =>
            option.setName('user')
                .setDescription('Target user')
                .setRequired(false))
];

const rest = new REST({ version: '10' }).setToken(TOKEN);
await rest.put(Routes.applicationCommands(CLIENT_ID), {
    body: commands.map(c => c.toJSON())
});

Guild Commands

Available instantly in specific servers (for testing):

await rest.put(Routes.applicationGuildCommands(CLIENT_ID, GUILD_ID), {
    body: commands.map(c => c.toJSON())
});

Command Options

String Options

new SlashCommandBuilder()
    .setName('echo')
    .setDescription('Repeat a message')
    .addStringOption(option =>
        option.setName('message')
            .setDescription('What to say')
            .setRequired(true)
            .setMaxLength(100))

Number Options

.addIntegerOption(option =>
    option.setName('amount')
        .setDescription('How many')
        .setMinValue(1)
        .setMaxValue(100))

Choice Options

.addStringOption(option =>
    option.setName('color')
        .setDescription('Pick a color')
        .addChoices(
            { name: 'Red', value: 'red' },
            { name: 'Blue', value: 'blue' },
            { name: 'Green', value: 'green' }
        ))

Handling Commands

client.on('interactionCreate', async interaction => {
    if (!interaction.isChatInputCommand()) return;
    
    switch (interaction.commandName) {
        case 'ping':
            await interaction.reply({
                content: `Pong! ${client.ws.ping}ms`,
                ephemeral: true  // Only visible to command user
            });
            break;
            
        case 'userinfo':
            const user = interaction.options.getUser('user') || interaction.user;
            await interaction.reply({
                embeds: [{
                    title: user.tag,
                    fields: [
                        { name: 'ID', value: user.id },
                        { name: 'Created', value: user.createdAt.toDateString() }
                    ]
                }]
            });
            break;
    }
});

Subcommands

Group related commands:

new SlashCommandBuilder()
    .setName('settings')
    .setDescription('Server settings')
    .addSubcommand(sub =>
        sub.setName('view')
            .setDescription('View current settings'))
    .addSubcommand(sub =>
        sub.setName('set')
            .setDescription('Change a setting')
            .addStringOption(opt =>
                opt.setName('key').setDescription('Setting name').setRequired(true))
            .addStringOption(opt =>
                opt.setName('value').setDescription('New value').setRequired(true)))

Autocomplete

Dynamic suggestions as the user types:

client.on('interactionCreate', async interaction => {
    if (interaction.isAutocomplete()) {
        const focused = interaction.options.getFocused();
        const choices = ['minecraft', 'rust', 'fivem', 'valheim'];
        const filtered = choices.filter(c => c.startsWith(focused.toLowerCase()));
        await interaction.respond(
            filtered.map(choice => ({ name: choice, value: choice }))
        );
    }
});

Permissions

Default Permissions

new SlashCommandBuilder()
    .setName('ban')
    .setDescription('Ban a user')
    .setDefaultMemberPermissions(PermissionFlagsBits.BanMembers)

DM Permissions

new SlashCommandBuilder()
    .setName('serverinfo')
    .setDMPermission(false)  // Only usable in servers

Deployment Tips

  1. Register global commands once (they persist until you update)
  2. Use guild commands during development (instant updates)
  3. Keep command descriptions clear and concise
  4. Use ephemeral replies for sensitive information
  5. Respond within 3 seconds or defer:
await interaction.deferReply();  // Buy time
// ... long operation ...
await interaction.editReply('Done!');

Host your bot on Space-Node for reliable 24/7 availability. Slash commands only work when your bot is online - consistent uptime ensures your commands are always responsive.

Jochem

About the Author

Jochem, Infrastructure Expert, expert in game server hosting, VPS infrastructure, and 24/7 streaming solutions with 5-10 years experience.

Since 2023
500+ servers hosted
4.8/5 avg rating

I specialize in Minecraft, FiveM, Rust, and 24/7 streaming infrastructure, operating enterprise-grade AMD Ryzen 9 hardware in Netherlands datacenters.

View my full bio and credentials →

Keep Your Bot Online 24/7

Reliable Discord bot hosting powered by enterprise AMD Ryzen 9 hardware. Start free, upgrade anytime with guaranteed uptime.

Discord Slash Commands: Complete Implementation Guide