Modals

Modals are pop-up forms shown in response to a command or component interaction. Build them with ModalBuilder and read submissions from...

Modals are pop-up forms shown in response to a command or component interaction. Build them with ModalBuilder and read submissions from ModalSubmitInteraction.

Building

import { ModalBuilder, TextInputStyle } from 'athena';
 
const modal = new ModalBuilder('Feedback', 'feedback-form');
 
modal.addTextInput({ custom_id: 'subject', style: TextInputStyle.Short, label: 'Subject', required: true, max_length: 100 });
modal.addTextInput({ custom_id: 'body', style: TextInputStyle.Paragraph, label: 'What happened', required: true, max_length: 4000 });
 
await interaction.createModal(modal.toJSON());

Constructor: title (max 45 chars) and custom_id (max 100 chars, reused on submission). Each addTextInput becomes its own row; Discord allows up to 5.

Text input fields

FieldNotes
custom_ididentifier within the modal
styleTextInputStyle.Short or TextInputStyle.Paragraph
labelshown above the input
placeholderhint text
min_length / max_lengthbounds, hard cap 4000
requireddefaults to false
valuepre-fill

File upload component

Discord's modal file upload, wrapped in a label:

modal.addFileUpload({ custom_id: 'screenshot', label: 'Attach a screenshot', min_values: 0, max_values: 3, required: false });

Showing a modal

A modal must be the first response to an interaction; you cannot defer first. It works from a command or a component interaction.

async handleCommand(context, interaction) {
  await interaction.createModal(modal.toJSON());
}

Handling submissions

A submission fires interactionCreate with a ModalSubmitInteraction. Read values from fields:

client.on('interactionCreate', async (interaction) => {
  if (!interaction.isModal() || interaction.data.custom_id !== 'feedback-form') return;
  const subject = interaction.fields.get('subject');
  const body = interaction.fields.get('body');
  await interaction.createMessage({ content: `Thanks: ${subject}`, flags: MessageFlags.Ephemeral });
});

With the commands framework, implement handleModal and route by custom_id prefix.

Tips

  • Encode any context you need into the custom_id (for example feedback-form:<userID>); modals carry no other state across the submit boundary.
  • You cannot show a second modal in response to a modal submission. Defer or send a normal message instead.