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 Space-Node Team – Infrastructure Team – 15+ years combined 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.

Space-Node Team

About the Author

Space-Node Team – Infrastructure Team – Experts in game server hosting, VPS infrastructure, and 24/7 streaming solutions with 15+ years combined experience.

Since 2023
500+ servers hosted
4.8/5 avg rating

Our team specializes in Minecraft, FiveM, Rust, and 24/7 streaming infrastructure, operating enterprise-grade AMD Ryzen 9 hardware in Netherlands datacenters. We maintain GDPR compliance and ISO 27001-aligned security standards.

View Space-Node's full team bio and credentials →

Launch Your VPS Today

Get started with professional VPS hosting powered by enterprise hardware. Instant deployment and 24/7 support included.

Discord Slash Commands: Complete Implementation Guide