LogoRobo.js

Commands

Create and manage slash commands

Slash commands let users interact with your bot through Discord's command interface. Robo.js routes commands from the file system and handles registration automatically.

This guide covers the essentials. For the complete reference including all config fields, min/max constraints, channel type restrictions, option localizations, and the built-in /help command, see the @robojs/discordjs Commands reference.

Discord slash command picker showing a list of registered slash commands including /ping, /greet, and /color, with command descriptions visible next to each command name

FocusThe slash command autocomplete picker with multiple commands listedZoom100%NotesOpen Discord in a server with a Robo.js bot that has several commands registered. Type / in the message input to open the command picker. Show at least 3-4 commands with their descriptions.

Creating Commands

Create a file in src/commands named after the command. The file's default export is the handler function.

ping.ts/ping
src/commands/ping.ts
export default () => {
	return 'Pong!'
}
src/commands/ping.js
export default () => {
	return 'Pong!'
}

Returning a value uses Sage mode (Robo.js automatically sends the return value as the reply). You can also use the interaction object directly.

src/commands/ping.ts
import type { ChatInputCommandInteraction } from 'discord.js'

export default (interaction: ChatInputCommandInteraction) => {
	interaction.reply('Pong!')
}
src/commands/ping.js
export default (interaction) => {
	interaction.reply('Pong!')
}

Both approaches register a /ping command when the file is named ping.ts.

Command Configuration

Export a config object using createCommandConfig() to set metadata like descriptions, options, and permissions.

src/commands/ping.ts
import { createCommandConfig } from '@robojs/discordjs'

export const config = createCommandConfig({
	description: 'Responds with Pong!'
})
src/commands/ping.js
import { createCommandConfig } from '@robojs/discordjs'

export const config = createCommandConfig({
	description: 'Responds with Pong!'
})
FieldTypeDescription
descriptionstringCommand description shown in Discord
contextsCommandContext[]Where the command is available
defaultMemberPermissionsPermissionResolvableRequired permissions
integrationTypesCommandIntegrationType[]Install types that can use this command
nsfwbooleanRestrict to age-gated channels
optionsCommandOption[]Command parameters
sagefalse | SageOptionsSage mode configuration
timeoutnumberMaximum execution time in milliseconds

Options

Define parameters with the options array in your config. Access parsed values through the second parameter using CommandOptions<typeof config>.

src/commands/greet.ts
import { createCommandConfig } from '@robojs/discordjs'
import type { ChatInputCommandInteraction, CommandOptions } from '@robojs/discordjs'

export const config = createCommandConfig({
	description: 'Greet someone',
	options: [
		{
			name: 'user',
			type: 'user',
			description: 'User to greet',
			required: true
		},
		{
			name: 'message',
			type: 'string',
			description: 'Custom greeting'
		}
	]
} as const)

export default (interaction: ChatInputCommandInteraction, options: CommandOptions<typeof config>) => {
	return `Hello ${options.user.username}! ${options.message ?? 'Welcome!'}`
}
src/commands/greet.js
import { createCommandConfig } from '@robojs/discordjs'

export const config = createCommandConfig({
	description: 'Greet someone',
	options: [
		{
			name: 'user',
			type: 'user',
			description: 'User to greet',
			required: true
		},
		{
			name: 'message',
			type: 'string',
			description: 'Custom greeting'
		}
	]
})

export default (interaction, options) => {
	return `Hello ${options.user.username}! ${options.message ?? 'Welcome!'}`
}

The as const assertion tells TypeScript to infer exact option names and types from your config, so CommandOptions can provide correct autocomplete and type checking.

Discord showing the /greet slash command being used with a user option autocomplete dropdown visible, listing server members to select from, and a message option field below

FocusThe command options fields with the user picker dropdown openZoom100%NotesType /greet in a server with the example bot. Show the command option fields: the user picker should be open showing a dropdown of server members, and the message field should be visible below.

Option Types

TypeTypeScript Type
stringstring
integernumber
numbernumber
booleanboolean
userUser
channelGuildBasedChannel
memberGuildMember | null
roleRole
attachmentAttachment
mentionGuildMember | Role

Choices

Restrict an option to a fixed set of values with the choices array.

src/commands/color.ts
import { createCommandConfig } from '@robojs/discordjs'

export const config = createCommandConfig({
	description: 'Chooses a color',
	options: [
		{
			name: 'color',
			description: 'Your favorite color',
			type: 'string',
			choices: [
				{ name: 'Red', value: 'red' },
				{ name: 'Green', value: 'green' },
				{ name: 'Blue', value: 'blue' }
			]
		}
	]
} as const)
src/commands/color.js
import { createCommandConfig } from '@robojs/discordjs'

export const config = createCommandConfig({
	description: 'Chooses a color',
	options: [
		{
			name: 'color',
			description: 'Your favorite color',
			type: 'string',
			choices: [
				{ name: 'Red', value: 'red' },
				{ name: 'Green', value: 'green' },
				{ name: 'Blue', value: 'blue' }
			]
		}
	]
})

Each choice has a name displayed to the user and a value passed to your handler.

Discord showing the /color slash command with a dropdown choice picker displaying Red, Green, and Blue as fixed options

FocusThe choice dropdown showing the three color optionsZoom100%NotesType /color in the command picker, then click the color option field. Capture the moment the dropdown is open with Red, Green, Blue choices visible.

Autocomplete

For dynamic suggestions, set autocomplete: true on the option and export an autocomplete function.

src/commands/color.ts
import type { AutocompleteInteraction } from 'discord.js'

const colors = ['red', 'green', 'blue', 'yellow', 'purple']

export const autocomplete = (interaction: AutocompleteInteraction) => {
	const focused = interaction.options.getFocused()
	const filtered = colors.filter((c) => c.startsWith(focused))
	return filtered.map((c) => ({ name: c, value: c }))
}
src/commands/color.js
const colors = ['red', 'green', 'blue', 'yellow', 'purple']

export const autocomplete = (interaction) => {
	const focused = interaction.options.getFocused()
	const filtered = colors.filter((c) => c.startsWith(focused))
	return filtered.map((c) => ({ name: c, value: c }))
}

The function receives an AutocompleteInteraction and returns matching choices as the user types.

Subcommands

Nest files in folders to create subcommands. The folder name becomes the parent command and each file becomes a subcommand.

lock.ts/channel lock
unlock.ts/channel unlock

This creates /channel lock and /channel unlock.

Discord slash command picker showing the /channel command expanded into subcommands, with lock and unlock listed as subcommand options beneath the parent command

FocusThe subcommand picker showing /channel with lock and unlock sub-optionsZoom100%NotesType /channel in the command picker. Discord should show the parent command with subcommands lock and unlock listed below it.

Subcommand Groups

Add another folder level for subcommand groups, creating a three-level hierarchy.

idle.ts/bot status idle

This creates /bot status idle.

Contexts and Integration Types

Control where your command is available with contexts and who can use it with integrationTypes. Use these when you want commands to work in DMs or with user-installed bots (installed by individual users rather than server admins).

src/commands/hello.ts
import { createCommandConfig } from '@robojs/discordjs'

export const config = createCommandConfig({
	description: 'Available everywhere',
	contexts: ['Guild', 'BotDM', 'PrivateChannel'],
	integrationTypes: ['GuildInstall', 'UserInstall']
})
src/commands/hello.js
import { createCommandConfig } from '@robojs/discordjs'

export const config = createCommandConfig({
	description: 'Available everywhere',
	contexts: ['Guild', 'BotDM', 'PrivateChannel'],
	integrationTypes: ['GuildInstall', 'UserInstall']
})
ContextDescription
GuildServer channels
BotDMDirect messages with the bot
PrivateChannelGroup DMs and other private channels
Integration TypeDescription
GuildInstallAvailable in servers where the bot is installed
UserInstallAvailable to users who installed the bot, in any server

Permissions

Use defaultMemberPermissions with Discord.js PermissionFlagsBits to restrict command access by server role.

src/commands/kick.ts
import { createCommandConfig } from '@robojs/discordjs'
import { PermissionFlagsBits } from 'discord.js'

export const config = createCommandConfig({
	description: 'Kick a member',
	defaultMemberPermissions: PermissionFlagsBits.KickMembers
})
src/commands/kick.js
import { createCommandConfig } from '@robojs/discordjs'
import { PermissionFlagsBits } from 'discord.js'

export const config = createCommandConfig({
	description: 'Kick a member',
	defaultMemberPermissions: PermissionFlagsBits.KickMembers
})

Server admins can override default permissions for their own server. Default permissions may not apply as expected to subcommands due to a Discord limitation.

Command Registration

Robo.js registers commands automatically when you run robo dev or robo build. It uses hash caching to detect changes and only re-registers when needed.

To force re-registration, use the --force flag.

npx robo build --force

This also cleans up commands that no longer exist in your src/commands directory.

Testing Commands

Test your commands programmatically with Mock Server, which simulates Discord's API locally and lets you verify command behavior without a live bot connection.

Next Steps

On this page