| name | deact-modal |
| description | Create or use Discord modals with Deact. Use when implementing form inputs and text submissions. |
Deact Modals
Overview
Modals in Deact require two parts:
- Modal builder function - Creates the Discord ModalBuilder (in
src/modals/)
- Deact integration - Opens modal and handles submission (in element)
Quick Reference
Modal builder location: src/modals/<category>Modals.js
Text input component: src/modals/components/textInputRow.js
Part 1: Modal Builder Function
Simple Text Input Modal
const { buildGenericTextInputModal } = require("./genericModals");
const buildMySearchModal = ({ id, title = "Search", value, required = true }) =>
buildGenericTextInputModal({
id,
textInputId: "searchInput",
title,
label: "Enter search term",
placeholder: "Type here...",
value,
required,
});
module.exports = { buildMySearchModal };
Custom Modal with Multiple Inputs
const {
ModalBuilder,
TextInputBuilder,
TextInputStyle,
ActionRowBuilder,
} = require("discord.js");
const buildCustomModal = ({ id, title = "Custom Form" }) => {
const modal = new ModalBuilder().setCustomId(id).setTitle(title);
const nameInput = new TextInputBuilder()
.setCustomId("nameInput")
.setLabel("Name")
.setStyle(TextInputStyle.Short)
.setRequired(true);
const descInput = new TextInputBuilder()
.setCustomId("descriptionInput")
.setLabel("Description")
.setStyle(TextInputStyle.Paragraph)
.setRequired(false);
modal.addComponents(
new ActionRowBuilder().addComponents(nameInput),
new ActionRowBuilder().addComponents(descInput),
);
return modal;
};
module.exports = { buildCustomModal };
Part 2: Using Modal in Deact Element
Step-by-Step Integration
const {
useState,
useCallbackBinding,
useModalSubmitCallbackBinding,
createModal,
createElement,
} = require("../../deact/deact");
const { ButtonStyle } = require("discord.js");
const Button = require("../../deact/elements/Button");
const { buildMySearchModal } = require("../../modals/myModals");
module.exports = async (ref, { initialValue }) => {
const [searchTerm, setSearchTerm] = useState(initialValue, ref);
const onSubmitKey = useModalSubmitCallbackBinding((interaction) => {
const value = interaction.fields.getTextInputValue("searchInput");
setSearchTerm(value);
}, ref);
const openModalKey = useCallbackBinding(
(interaction) => {
return createModal(
buildMySearchModal,
{ title: "Search", value: searchTerm },
onSubmitKey,
interaction,
ref,
);
},
ref,
{ defer: false },
);
return {
contents: [searchTerm ? `Searching: ${searchTerm}` : "Click to search"],
components: [
[
createElement(Button, {
emoji: "🔎",
label: "Search",
callbackBindingKey: openModalKey,
style: searchTerm ? ButtonStyle.Primary : ButtonStyle.Secondary,
}),
],
],
};
};
createModal API
createModal(
modalBuilderFunction,
props,
submitCallbackKey,
interaction,
ref,
data,
);
Retrieving Form Values
In the submit callback, use interaction.fields.getTextInputValue(inputId):
const onSubmitKey = useModalSubmitCallbackBinding((interaction, data) => {
const name = interaction.fields.getTextInputValue("nameInput");
const description = interaction.fields.getTextInputValue("descriptionInput");
console.log(data.extraInfo);
setFormData({ name, description });
}, ref);
Complete Example: Search with Clear
const {
useState,
useCallbackBinding,
useModalSubmitCallbackBinding,
createModal,
createElement,
} = require("../../deact/deact");
const { ButtonStyle } = require("discord.js");
const Button = require("../../deact/elements/Button");
const { buildGenericTextInputModal } = require("../../modals/genericModals");
const buildSearchModal = ({ id, value }) =>
buildGenericTextInputModal({
id,
textInputId: "query",
title: "Search",
label: "Search Query",
placeholder: "Enter search term...",
value,
required: false,
});
module.exports = async (ref, {}) => {
const [query, setQuery] = useState("", ref);
const submitKey = useModalSubmitCallbackBinding((interaction) => {
setQuery(interaction.fields.getTextInputValue("query"));
}, ref);
const openKey = useCallbackBinding(
(interaction) =>
createModal(
buildSearchModal,
{ value: query },
submitKey,
interaction,
ref,
),
ref,
{ defer: false },
);
const clearKey = useCallbackBinding(() => setQuery(""), ref);
return {
contents: [query ? `Results for: "${query}"` : "No search active"],
components: [
[
createElement(Button, {
emoji: "🔎",
callbackBindingKey: openKey,
style: query ? ButtonStyle.Primary : ButtonStyle.Secondary,
}),
createElement(Button, {
emoji: "❌",
label: "Clear",
callbackBindingKey: clearKey,
disabled: !query,
}),
],
],
};
};
Key Rules
defer: false is required for the button that opens the modal
- Modal builder receives
id from Deact - don't set it manually
- Use
useModalSubmitCallbackBinding for submit handlers
- Return
createModal() from the open callback
textInputId must match between builder and getTextInputValue()