feat(guide): port legacy guide (#10938)

* feat: initial attempt at porting legacy guide

* feat: completion of legacy guide backport

* chore: lockfile shenanigans

* fix: handle svgs

* fix: replace svg with mermaid integration

* chore: format

* chore: remove unnecssary bullet

* chore: cleanup code highlights

* chore: explicit return

* chore: move display components after interactive components in sidebar

* chore: voice

* top link should be installation
* add docs link to sidebar

* feat: subguide-based accent styles

* chore: don't list faq twice

* chore: mention display components in interactive components

* fix: remove unoccs/order rule from guide

* chore: redirect to legacy guide instead of /guide root

* refactor: use `<kbd>`

* refactor: more kbd use

* Update apps/guide/content/docs/legacy/app-creation/handling-events.mdx

Co-authored-by: Naiyar <137700126+imnaiyar@users.noreply.github.com>

* chore: fix typos

Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com>

* chore: fix typos

* chore: fix links regarding secret stores across coding platforms

* chore: fix typo

* chore: link node method directly

Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com>

* chore: typos

Co-authored-by: Vlad Frangu <me@vladfrangu.dev>

* chore: typo

Co-authored-by: Vlad Frangu <me@vladfrangu.dev>

* fix: prevent v14 changes from being listed twice

* chore: prefer relative links

* chore: missed link conversion

* chore: missed link conversion

* chore: fix link

* chore: remove legacy code highlight markers

* chore: rephrase and extend contributing guidelines

* feat(setup): suggest cli flag over dotenv package

* chore: move introduction in sidebar

better navigation experience if the 'next page' in intro refers to getting started vs. updating/faq

* fix: replace outdated link

* fix: update voice dependencies

* chore: update node install instructions

* fix: list in missing access callout

* chore: match bun env file format

* chore: restore ffmpeg disclaimer

* fix: lockfile conflict

* chore: action row typo

Co-authored-by: Vlad Frangu <me@vladfrangu.dev>

* chore: no longer use at-next for pino

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com>
Co-authored-by: Naiyar <137700126+imnaiyar@users.noreply.github.com>
Co-authored-by: Vlad Frangu <me@vladfrangu.dev>
This commit is contained in:
Souji
2025-07-08 15:01:50 +02:00
committed by GitHub
parent ee3ca6f7c6
commit bc6005f446
136 changed files with 11847 additions and 48 deletions

View File

@@ -0,0 +1,52 @@
---
title: Context Menus
---
Context Menus are application commands which appear when right clicking or tapping a user or a message, in the Apps submenu.
<Callout>
This page is a follow-up to the [slash commands](../slash-commands/advanced-creation) section. Please carefully read
those pages first so that you can understand the methods used in this section.
</Callout>
## Registering context menu commands
To create a context menu command, use the `ContextMenuCommandBuilder` class. You can then set the type of the context menu (user or message) using the `setType()` method.
```js
const { ContextMenuCommandBuilder, ApplicationCommandType } = require('discord.js');
const data = new ContextMenuCommandBuilder().setName('User Information').setType(ApplicationCommandType.User);
```
## Receiving context menu command interactions
Context menus commands, just like slash commands, are received via an interaction. You can check if a given interaction is a context menu by invoking the `isContextMenuCommand()` method, or the `isMessageContextMenuCommand()` and `isUserContextMenuCommand()` methods to check for the specific type of context menu interaction:
```js
client.on(Events.InteractionCreate, (interaction) => {
if (!interaction.isUserContextMenuCommand()) return;
console.log(interaction);
});
```
## Extracting data from context menus
For user context menus, you can get the targeted user by accessing the `targetUser` or `targetMember` property from the `UserContextMenuCommandInteraction`.
For message context menus, you can get the targeted message by accessing the `targetMessage` property from the `MessageContextMenuCommandInteraction`.
```js
client.on(Events.InteractionCreate, (interaction) => {
if (!interaction.isUserContextMenuCommand()) return;
// Get the User's username from context menu
const { username } = interaction.targetUser;
console.log(username);
});
```
## Notes
- Context menu commands cannot have subcommands or any options.
- Responding to context menu commands functions the same as slash commands. Refer to our [slash command responses](../slash-commands/response-methods) guide for more information.
- Context menu command permissions also function the same as slash commands. Refer to our [slash command permissions](../slash-commands/permissions) guide for more information.

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -0,0 +1,3 @@
{
"title": "Other Interactions"
}

View File

@@ -0,0 +1,192 @@
---
title: Modals
---
With modals you can create pop-up forms that allow users to provide you with formatted inputs through submissions. We'll cover how to create, show, and receive modal forms using discord.js!
<Callout>
This page is a follow-up to the [interactions (slash commands) page](../slash-commands/advanced-creation). Please
carefully read that section first, so that you can understand the methods used in this section.
</Callout>
## Building and responding with modals
Unlike message components, modals aren't strictly components themselves. They're a callback structure used to respond to interactions.
<Callout>
You can have a maximum of five `ActionRowBuilder`s per modal builder, and one `TextInputBuilder` within an
`ActionRowBuilder`. Currently, you can only use `TextInputBuilder`s in modal action rows builders.
</Callout>
To create a modal you construct a new `ModalBuilder`. You can then use the setters to add the custom id and title.
```js
const { Events, ModalBuilder } = require('discord.js');
client.on(Events.InteractionCreate, async (interaction) => {
if (!interaction.isChatInputCommand()) return;
if (interaction.commandName === 'ping') {
const modal = new ModalBuilder().setCustomId('myModal').setTitle('My Modal');
// TODO: Add components to modal...
}
});
```
<Callout>
The custom id is a developer-defined string of up to 100 characters. Use this field to ensure you can uniquely define
all incoming interactions from your modals!
</Callout>
The next step is to add the input fields in which users responding can enter free-text. Adding inputs is similar to adding components to messages.
At the end, we then call `ChatInputCommandInteraction#showModal` to display the modal to the user.
<Callout type="warn">
If you're using typescript you'll need to specify the type of components your action row holds. This can be done by specifying the generic parameter in `ActionRowBuilder`:
```diff
- new ActionRowBuilder()
+ new ActionRowBuilder<ModalActionRowComponentBuilder>()
```
</Callout>
```js
const { ActionRowBuilder, Events, ModalBuilder, TextInputBuilder, TextInputStyle } = require('discord.js');
client.on(Events.InteractionCreate, async (interaction) => {
if (!interaction.isChatInputCommand()) return;
if (interaction.commandName === 'ping') {
// Create the modal
const modal = new ModalBuilder().setCustomId('myModal').setTitle('My Modal');
// Add components to modal
// Create the text input components
const favoriteColorInput = new TextInputBuilder()
.setCustomId('favoriteColorInput')
// The label is the prompt the user sees for this input
.setLabel("What's your favorite color?")
// Short means only a single line of text
.setStyle(TextInputStyle.Short);
const hobbiesInput = new TextInputBuilder()
.setCustomId('hobbiesInput')
.setLabel("What's some of your favorite hobbies?")
// Paragraph means multiple lines of text.
.setStyle(TextInputStyle.Paragraph);
// An action row only holds one text input,
// so you need one action row per text input.
const firstActionRow = new ActionRowBuilder().addComponents(favoriteColorInput);
const secondActionRow = new ActionRowBuilder().addComponents(hobbiesInput);
// Add inputs to the modal
modal.addComponents(firstActionRow, secondActionRow);
// Show the modal to the user
await interaction.showModal(modal); // [!code word:showModal]
}
});
```
Restart your bot and invoke the `/ping` command again. You should see a popup form resembling the image below:
![Modal Example](./images/modal-example.png)
<Callout type="warn">
Showing a modal must be the first response to an interaction. You cannot `defer()` or `deferUpdate()` then show a
modal later.
</Callout>
### Input styles
Currently there are two different input styles available:
- `Short`, a single-line text entry;
- `Paragraph`, a multi-line text entry similar to the HTML `<textarea>`;
### Input properties
In addition to the `customId`, `label` and `style`, a text input can be customised in a number of ways to apply validation, prompt the user, or set default values via the `TextInputBuilder` methods:
```js
const input = new TextInputBuilder()
// set the maximum number of characters to allow
.setMaxLength(1_000)
// set the minimum number of characters required for submission
.setMinLength(10)
// set a placeholder string to prompt the user
.setPlaceholder('Enter some text!')
// set a default value to pre-fill the input
.setValue('Default')
// require a value in this input field
.setRequired(true);
```
## Receiving modal submissions
### Interaction collectors
Modal submissions can be collected within the scope of the interaction that showed it by utilising an `InteractionCollector`, or the `ChatInputCommandInteraction#awaitModalSubmit` promisified method. These both provide instances of the `ModalSubmitInteraction` class as collected items.
For a detailed guide on receiving message components via collectors, please refer to the [collectors guide](../popular-topics/collectors#interaction-collectors).
### The interactionCreate event
To receive a `ModalSubmitInteraction` event, attach an `Client#interactionCreate` event listener to your client and use the `BaseInteraction#isModalSubmit` type guard to make sure you only receive modals:
```js
client.on(Events.InteractionCreate, (interaction) => {
if (!interaction.isModalSubmit()) return;
console.log(interaction);
});
```
## Responding to modal submissions
The `ModalSubmitInteraction` class provides the same methods as the `ChatInputCommandInteraction` class. These methods behave equally:
- `reply()`
- `editReply()`
- `deferReply()`
- `fetchReply()`
- `deleteReply()`
- `followUp()`
If the modal was shown from a `ButtonInteraction` or `StringSelectMenuInteraction`, it will also provide these methods, which behave equally:
- `update()`
- `deferUpdate()`
```js
client.on(Events.InteractionCreate, async (interaction) => {
if (!interaction.isModalSubmit()) return;
if (interaction.customId === 'myModal') {
await interaction.reply({ content: 'Your submission was received successfully!' });
}
});
```
<Callout>
If you're using typescript, you can use the `ModalSubmitInteraction#isFromMessage` typeguard, to make sure the
received interaction was from a `MessageComponentInteraction`.
</Callout>
## Extracting data from modal submissions
You'll most likely need to read the data sent by the user in the modal. You can do this by accessing the `ModalSubmitInteraction#fields`. From there you can call `ModalSubmitFields#getTextInputValue` with the custom id of the text input to get the value.
```js
client.on(Events.InteractionCreate, (interaction) => {
if (!interaction.isModalSubmit()) return;
// Get the data entered by the user
const favoriteColor = interaction.fields.getTextInputValue('favoriteColorInput');
const hobbies = interaction.fields.getTextInputValue('hobbiesInput');
console.log({ favoriteColor, hobbies });
});
```