Ein discord modal ist der sauberste Weg, strukturierte Eingaben von einem Nutzer einzusammeln, ohne einen Kanal mit Hin-und-her-Nachrichten zu fluten. Die Ticket-Formulare, Bewerbungssysteme, Feedback-Boxen und „Vorschlag senden"-Abläufe, die du auf aktiven Servern siehst, basieren fast immer auf einem Modal: Der Nutzer klickt auf einen Button oder führt einen Befehl aus, ein kleines Pop-up-Formular öffnet sich in der Bildschirmmitte, er füllt die Felder aus und sendet ab. In diesem Leitfaden zeige ich Schritt für Schritt, wie man mit discord.js ein Modal von Grund auf baut, die Felder validiert und die gesendeten Daten verarbeitet.
Was ist ein Modal und wann benutzt man es
Ein Modal ist Discords Pop-up-Formular-Komponente. Es lässt sich als Reaktion auf eine Interaktion wie einen Button, ein Auswahlmenü oder einen Slash-Befehl öffnen. Ein Modal kann maximal 5 Texteingabefelder enthalten, und jedes Feld ist entweder einzeilig (Short) oder mehrzeilig (Paragraph).
- Gute Verwendung: ein Ticket öffnen, ein Bewerbungsformular, ein Bugreport oder alles, was Freitext erfordert, etwa einen Namen oder einen Grund.
- Schlechte Verwendung: Ja/Nein-Fragen oder eine feste Auswahl — hier ist ein Button oder ein Auswahlmenü das richtige Werkzeug.
Eine wichtige Einschränkung: Du kannst ein Modal nur als direkte Antwort auf eine Nutzerinteraktion öffnen. Du kannst nicht zuerst eine Nachricht senden und dann ein Modal öffnen; der Aufruf interaction.showModal() muss die erste Antwort auf diese Interaktion sein.
Voraussetzungen und Einrichtung
Dieses Beispiel richtet sich an discord.js v14. Stelle sicher, dass Node.js 18 oder neuer installiert ist. In einem leeren Ordner:
npm init -y
npm install discord.js
Ich gehe davon aus, dass du deine Bot-Anwendung bereits im Discord Developer Portal erstellt, den Token geholt und den Bot mit dem Scope applications.commands auf deinen Server eingeladen hast.
Das Modal bauen
Ein Modal besteht aus vier Bausteinen: ModalBuilder (das Fenster selbst), TextInputBuilder (jedes Feld), TextInputStyle (kurz/lang) und einem ActionRowBuilder, der die Felder umhüllt. Jedes Textfeld kommt in seine eigene Action Row.
const {
ModalBuilder,
TextInputBuilder,
TextInputStyle,
ActionRowBuilder,
} = require('discord.js');
function buildSupportModal() {
const modal = new ModalBuilder()
.setCustomId('support_modal')
.setTitle('Support-Anfrage');
const betreff = new TextInputBuilder()
.setCustomId('betreff')
.setLabel('Betreff')
.setStyle(TextInputStyle.Short)
.setMinLength(3)
.setMaxLength(80)
.setRequired(true);
const beschreibung = new TextInputBuilder()
.setCustomId('beschreibung')
.setLabel('Beschreibe dein Problem')
.setStyle(TextInputStyle.Paragraph)
.setPlaceholder('Gib so viele Details wie möglich an...')
.setMaxLength(1000)
.setRequired(true);
modal.addComponents(
new ActionRowBuilder().addComponents(betreff),
new ActionRowBuilder().addComponents(beschreibung),
);
return modal;
}
Die customId-Werte sind hier entscheidend: Sowohl das Modal als auch jedes Feld brauchen eine eindeutige customId, denn wir lesen die gesendeten Daten später genau mit diesen Bezeichnern wieder aus.
Das Modal öffnen
Öffnen wir das Modal über einen Slash-Befehl. Wenn die Befehlsinteraktion eintrifft, rufen wir showModal auf. Achtung: Wenn du ein Modal anzeigen willst, rufe vorher nicht deferReply oder reply auf — das Modal muss die allererste Antwort der Interaktion sein.
client.on('interactionCreate', async (interaction) => {
if (interaction.isChatInputCommand() && interaction.commandName === 'support') {
await interaction.showModal(buildSupportModal());
}
});
Du kannst dasselbe Modal auch über einen Button öffnen; der einzige Unterschied ist, dass du den Interaktionstyp mit interaction.isButton() prüfst. Der Rest der Logik bleibt gleich.
Die Übermittlung verarbeiten und validieren
Wenn der Nutzer das Formular ausfüllt und absendet, trifft eine neue Interaktion ein. Wir fangen sie mit interaction.isModalSubmit() ab, unterscheiden über customId, um welches Modal es sich handelt, und lesen die Feldwerte mit fields.getTextInputValue() aus.
client.on('interactionCreate', async (interaction) => {
if (!interaction.isModalSubmit()) return;
if (interaction.customId !== 'support_modal') return;
const betreff = interaction.fields.getTextInputValue('betreff').trim();
const beschreibung = interaction.fields.getTextInputValue('beschreibung').trim();
// Discord erzwingt min/max length; füge trotzdem eigene Prüfungen hinzu
if (betreff.length < 3) {
return interaction.reply({
content: 'Der Betreff wirkt zu kurz.',
ephemeral: true,
});
}
// Daten hier verarbeiten: in DB speichern, in einen Kanal posten, Ticket öffnen...
await interaction.reply({
content: `Deine Anfrage ist eingegangen! Betreff: **${betreff}**`,
ephemeral: true,
});
});
Discord erzwingt deine setMinLength/setMaxLength- und setRequired-Regeln bereits clientseitig, aber die echte Validierung — etwa das Format einer E-Mail oder der Bereich einer Zahl — liegt bei dir. Bei ungültiger Eingabe ist eine Antwort mit ephemeral: true, sodass nur der Nutzer die Warnung sieht, der sauberste Ansatz.
Die Daten sinnvoll nutzen: Ticket-Beispiel
In den meisten Projekten werden die Modal-Daten als Embed in einen Log-Kanal gepostet oder in eine Datenbank geschrieben. Zum Beispiel die eingehende Anfrage in einen Team-Kanal einkippen:
const { EmbedBuilder } = require('discord.js');
const logKanal = interaction.guild.channels.cache.get('LOG_KANAL_ID');
const embed = new EmbedBuilder()
.setTitle('Neue Support-Anfrage')
.addFields(
{ name: 'Betreff', value: betreff },
{ name: 'Beschreibung', value: beschreibung },
{ name: 'Von', value: `${interaction.user}` },
)
.setTimestamp();
await logKanal.send({ embeds: [embed] });
Ab hier kannst du den Ablauf beliebig erweitern: Button-Aktionen wie „Schließen" oder „Übernehmen" hinzufügen, die Anfrage in MySQL/SQLite speichern oder sie an eine externe API weiterleiten.
Häufige Fehler
- reply/defer vor dem Anzeigen des Modals aufrufen: Das wirft „Interaction has already been acknowledged". Das Modal muss immer die erste Antwort sein.
- customId-Kollisionen: Verschiedenen Modals dieselbe id zu geben, zerbricht dein Submit-Routing. Gib jedem Modal und jedem Feld eine eindeutige id.
- Die 3-Sekunden-Regel: Auch auf eine Modal-Übermittlung musst du schnell antworten. Für lange Arbeit rufe zuerst
interaction.deferReply()auf und nutze danneditReply. - Das Limit von 5 Feldern überschreiten: Discord erlaubt maximal 5 Felder in einem Modal; brauchst du mehr, musst du den Ablauf aufteilen.
Häufige Fragen
Wie viele Felder kann ein Modal haben?
Maximal 5 Texteingabefelder. Jedes Feld muss in seinem eigenen ActionRowBuilder sitzen und kann nur ein Textfeld sein; Buttons oder Auswahlmenüs lassen sich in einem Modal nicht verwenden.
Kann ich ein Dropdown (Auswahlmenü) in einem Modal verwenden?
Nein. Modals unterstützen derzeit nur Textfelder. Wenn du eine Auswahl brauchst, zeige zuerst ein Auswahlmenü und öffne das Modal nach der Auswahl, oder hole die Auswahl in einem separaten Schritt außerhalb des Modals ein.
Warum kann ich die gesendeten Daten nicht auslesen?
Meist ist die id, die du an getTextInputValue() übergibst, nicht identisch mit dem setCustomId()-Wert des Feldes. Stelle sicher, dass beide exakt übereinstimmen.
Willst du einen professionellen Discord-Bot für deinen Server? Ich baue Ticketsysteme, Bewerbungsformulare und modal-basierte Abläufe sauber und wartbar. Melde dich, um über dein Projekt zu sprechen.