Nubis / Modals
Build
Modals
Forms that pop up over Discord, with typed fields and the same state as components.
A modal is defined in components/ like a button, and opened with showModal() from a command, a component or a context menu.
// components/report.ts
import { modal, textInput } from "nubis";
import { z } from "zod";
export const report = modal({
id: "report",
title: "Report a message",
state: z.object({ messageId: z.string() }),
fields: {
reason: textInput({ label: "What's wrong?", style: "paragraph", required: true, maxLength: 1000 }),
contact: textInput({ label: "How can we reach you?" }),
},
onSubmit: ({ state, values, reply }) => {
// values.reason: string · values.contact: string | undefined
return reply({ content: "Thanks, we'll take a look.", ephemeral: true });
},
});// menus/report-message.ts
import { messageCommand } from "nubis";
import { report } from "../components/report";
export default messageCommand({
name: "Report message",
run: ({ target, showModal }) => showModal(report({ messageId: target.id })),
});Fields
fields holds up to five textInputs, by key. The key is the field's id and the property you read from values. A required: true field is a string; any other is string | undefined, and undefined when left empty.
| Setting | What it does |
|---|---|
label | Shown above the field. |
description | Shown under the label. |
style | "short" for one line (the default), "paragraph" for a box. |
placeholder | Shown while empty. |
required | Must be filled in. |
minLength, maxLength | Length limits. |
Prefilled values
A modal call can prefill fields or change its title:
showModal(editNote({ noteId }, { values: { text: note.text } }));Opening it first
Discord only lets a modal be the first response to an interaction. Call showModal() before reply() or defer(), and before the two seconds after which Nubis shows "thinking…" — fetch what you need after the form is submitted, not before opening it.
The submit context
onSubmit gets state, values, reply, editReply, defer, api and interaction. A modal opened from a component also gets update(), which edits the message that component is on.
Modals take guards, middleware and store like components.