NewSchokr 13 is here. A whole new UI, seven locations, snapshots and more. Read more →

How to register slash commands in discord.js (global vs guild)

Create slash commands with SlashCommandBuilder in discord.js v14, register them globally or to one server, and handle them in interactionCreate.

Updated · 2 min read

In discord.js v14 you define slash commands with SlashCommandBuilder, register them with Discord (either globally or for a single server), and respond to them in the interactionCreate event. Registering and handling are separate steps: a command can be registered but unhandled, or handled but never registered.

Global vs guild commands

Guild commandsGlobal commands
Where they appearOne server onlyEvery server the bot is in, plus DMs if allowed
Update speedImmediatelyCan take longer to show up everywhere
Best forTesting and private botsPublic bots in production

A common workflow is to register to your test server while developing, then switch to global when you're done. If you register the same command both ways, it appears twice in your test server. Clear the guild set to fix that (shown below).

Define and register commands on startup

The simplest approach on Schokr is to register commands when the bot starts, so you don't need a separate deploy script. Registering with set replaces the whole list, so removed commands disappear too.

require('dotenv').config();
const { Client, Events, GatewayIntentBits, SlashCommandBuilder } = require('discord.js');

const commands = [
  new SlashCommandBuilder()
    .setName('ping')
    .setDescription('Replies with Pong!'),
  new SlashCommandBuilder()
    .setName('echo')
    .setDescription('Repeats what you say')
    .addStringOption((option) =>
      option.setName('text').setDescription('What to repeat').setRequired(true),
    ),
].map((command) => command.toJSON());

const client = new Client({ intents: [GatewayIntentBits.Guilds] });

client.once(Events.ClientReady, async (readyClient) => {
  const guildId = process.env.GUILD_ID;
  if (guildId) {
    await readyClient.application.commands.set(commands, guildId); // one server
  } else {
    await readyClient.application.commands.set(commands); // global
  }
  console.log(`Registered ${commands.length} commands as ${readyClient.user.tag}`);
});

client.on(Events.InteractionCreate, async (interaction) => {
  if (!interaction.isChatInputCommand()) return;

  if (interaction.commandName === 'ping') {
    await interaction.reply('Pong!');
  } else if (interaction.commandName === 'echo') {
    await interaction.reply(interaction.options.getString('text', true));
  }
});

client.login(process.env.DISCORD_TOKEN);

Add GUILD_ID=your-test-server-id to .env while testing. To get a server ID, enable Developer Mode in Discord's Advanced settings, then right-click the server and copy its ID.

Using a separate deploy script instead

Larger bots often register with the REST API from a script you run only when commands change:

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

const rest = new REST().setToken(process.env.DISCORD_TOKEN);

(async () => {
  // Guild: Routes.applicationGuildCommands(CLIENT_ID, GUILD_ID)
  // Global: Routes.applicationCommands(CLIENT_ID)
  const data = await rest.put(Routes.applicationCommands(process.env.CLIENT_ID), { body: commands });
  console.log(`Registered ${data.length} commands`);
})();

CLIENT_ID is your Application ID. You can run this from your own computer; it doesn't have to run on the server.

Removing duplicate or old commands

To clear the guild copy after moving to global:

await readyClient.application.commands.set([], process.env.GUILD_ID);

Command names and limits

  • Names must be lowercase, 1 to 32 characters, with no spaces (use - or _).
  • Descriptions are required, up to 100 characters.
  • Required options must come before optional ones.

Commands don't appear?

  • Re-invite the bot with the applications.commands scope. See inviting your bot.
  • For global commands, wait a bit and restart your Discord client (Ctrl+R on desktop).
  • Check the console for a registration error, such as an invalid name.
  • If the command shows but says "The application did not respond", see fixing "This interaction failed".