Interactions

Context Menus

Learn how to create context menu commands with djs-core.

Context menus allow users to interact with Discord elements (users or messages) by right-clicking on them. They provide quick access to bot functionality directly from the context.

Context menus appear when users right-click on Discord elements. They're perfect for quick actions like reporting, getting user info, or message moderation.

Creating a Context Menu

Context menus in djs-core are created using the ContextMenu class. Each context menu file in src/interactions/contexts/ automatically becomes available in Discord.

There are two types of context menus: User context menus (right-click on users) and Message context menus (right-click on messages). Choose the appropriate type based on what element users will interact with.

User Context Menu

A user context menu appears when right-clicking on a user:

import { ContextMenu } from "@djs-core/runtime";
import { ApplicationCommandType } from "discord.js";

export default new ContextMenu()
    .withType(ApplicationCommandType.User)
    .run(async (interaction) => {
        const user = interaction.targetUser;
        
        await interaction.reply({
            content: `Hello, ${user.username}!`,
        });
    });

File location: src/interactions/contexts/user/hi.ts

ApplicationCommandType.User
ApplicationCommandType
User context menu - Appears when right-clicking on a user. Access via interaction.targetUser and interaction.targetMember.
ApplicationCommandType.Message
ApplicationCommandType
Message context menu - Appears when right-clicking on a message. Access via interaction.targetMessage.

Message Context Menu

A message context menu appears when right-clicking on a message:

import { ContextMenu } from "@djs-core/runtime";
import { ApplicationCommandType } from "discord.js";

export default new ContextMenu()
    .withType(ApplicationCommandType.Message)
    .run(async (interaction) => {
        const message = interaction.targetMessage;
        
        // Add a reaction to the message
        await message.react("👍");
        
        await interaction.reply({
            content: "Message liked!",
            ephemeral: true,
        });
    });

File location: src/interactions/contexts/message/like.ts

Context Menu Organization

Context menus are organized by type in the file structure. The folder structure determines where the context menu appears:

src/interactions/contexts/user/hi.ts
import { ContextMenu } from "@djs-core/runtime";
import { ApplicationCommandType } from "discord.js";

export default new ContextMenu()
  .withType(ApplicationCommandType.User)
  .run(async (interaction) => {
    await interaction.reply(`Hi, ${interaction.targetUser.username}!`);
  });
File PathContext Menu
src/interactions/contexts/user/hi.tsRight-click user → "Hi"
src/interactions/contexts/user/info.tsRight-click user → "Info"
src/interactions/contexts/message/like.tsRight-click message → "Like"
src/interactions/contexts/message/report.tsRight-click message → "Report"

Accessing Target Data

User Context Menu

When using ApplicationCommandType.User, you can access the targeted user:

import { ContextMenu } from "@djs-core/runtime";
import { ApplicationCommandType } from "discord.js";

export default new ContextMenu()
    .withType(ApplicationCommandType.User)
    .run(async (interaction) => {
        const user = interaction.targetUser;
        const member = interaction.targetMember; // Guild member if in a server
        
        await interaction.reply({
            content: `User: ${user.tag}\nID: ${user.id}`,
        });
    });

Message Context Menu

When using ApplicationCommandType.Message, you can access the targeted message:

import { ContextMenu } from "@djs-core/runtime";
import { ApplicationCommandType } from "discord.js";

export default new ContextMenu()
    .withType(ApplicationCommandType.Message)
    .run(async (interaction) => {
        const message = interaction.targetMessage;
        const author = message.author;
        
        await interaction.reply({
            content: `Message by ${author.tag}: ${message.content}`,
        });
    });

Permissions

You can restrict context menus to users with specific permissions:

import { ContextMenu } from "@djs-core/runtime";
import { ApplicationCommandType, PermissionFlagsBits } from "discord.js";

export default new ContextMenu()
    .withType(ApplicationCommandType.Message)
    .setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages)
    .run(async (interaction) => {
        await interaction.targetMessage.delete();
        await interaction.reply({
            content: "Message deleted!",
            ephemeral: true,
        });
    });

DM Permission

You can control whether context menus are available in DMs:

import { ContextMenu } from "@djs-core/runtime";
import { ApplicationCommandType } from "discord.js";

export default new ContextMenu()
    .withType(ApplicationCommandType.User)
    .setDMPermission(false) // Only available in servers
    .run(async (interaction) => {
        await interaction.reply("Server-only context menu!");
    });

Type Safety

djs-core provides full TypeScript support for context menus. The interaction type is automatically inferred based on the menu type, giving you autocomplete and type checking:

TypeScript will automatically know which properties are available based on the context menu type. For User menus, you'll have access to targetUser and targetMember. For Message menus, you'll have access to targetMessage.
import { ContextMenu } from "@djs-core/runtime";
import { ApplicationCommandType } from "discord.js";

export default new ContextMenu()
    .withType(ApplicationCommandType.User)
    .run(async (interaction) => {
        // interaction is typed as UserContextMenuCommandInteraction
        // interaction.targetUser is available
        // interaction.targetMember is available (in guilds)
        
        const user = interaction.targetUser; // ✅ Type-safe
        // const message = interaction.targetMessage; // ❌ Type error
    });