Skip to main content
A button is declared with button(), in any file of src/, and exported. It says what the button looks like and what to do when someone clicks it; you never write an id.
src/components/again.ts
Import it where you send a message, and give it its data:
src/commands/ping.ts
That is all: the name of the export (again; for a default export, the name of the file) is what Discord sends back when the button is clicked, with the data the button carries. ChapterJS finds the button, checks that the data still matches, and runs your function with data.count as a number. Buttons that follow each other are put in rows for you, 5 per row.
Everything a button declares, and the types behind it, are in the reference. The interaction of a button is a ComponentInteraction.

What a button carries

data is what the button keeps between the message that shows it and the click: a name and a kind for each value, 'string', 'number' or 'boolean'. You give the values when you put the button in a message; run receives them typed, whatever happened meanwhile (a restart, another process). Discord keeps them in the button itself, so there is nothing to store. A value can have a default, which makes it optional where the button is put in a message: data: { page: { type: 'number', default: 1 } }. Then next(), next({}) and next alone carry page: 1, next({ page: 7 }) carries 7, and run always receives a number. A wrong default (default: 'one' on a 'number') is underlined, and refused when the file loads.
src/components/ban.ts
src/commands/mod/ban.ts
Discord gives 100 characters to the id of a button and its data together. Carrying too much is an error where the button is made, with how much room there is:
A button without data is used as is, without calling it: components: [confirm].

How it looks

label, emoji, style ('primary', 'secondary', 'success' or 'danger') and disabled are the look of the button. The declaration gives the default; where you put the button in a message, a second argument changes it for that message only:
src/components/next.ts
A button that opens a link is not declared: nothing runs in your bot when it is clicked. Write it where the message is, with linkButton():
src/commands/docs.ts

Answering a click

The interaction of a button has everything the interaction of a command has (reply(), defer(), edit(), followUp(), delete()), plus three things: interaction.message is the message the button is on, as a Message. Discord wants an answer within 3 seconds. After 2 seconds without one, ChapterJS answers that nothing changes for now, the person sees nothing, and your update() or reply() completes it later. A button that finishes without answering is a warning in your terminal, since the person reads “This interaction failed”:

Who may click

By default, everyone who sees the message can click. who: 'author' keeps a button for the person who used the command the message answers; anyone else reads “Only the person who used the command can use this button.”, privately.
src/components/cancel.ts
Like a command, a button has a where ('guild' by default, 'dm' or 'both'), which decides what run receives about the place: see Servers and private messages, it works the same.

When the button changes

A button stays in the messages it was sent in. When its export is renamed or removed, a click on an old message answers “This button is not available any more.”; when its data changed shape, “This button is out of date.”. Your bot never crashes for a button it no longer knows. Moving the file changes nothing: the name of the export is what counts.

Select menus

Let people pick among options, users, roles or channels.

Forms

Ask for text and choices in a form that opens in Discord.