> ## Documentation Index
> Fetch the complete documentation index at: https://www.chapterjs.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Buttons

> Add buttons to your messages, run code when they are clicked, and change the message they are on

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.

```ts src/components/again.ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import { button } from 'chapterjs';

const again = button({
  label: 'Again',
  data: { count: 'number' },
  async run({ interaction, data }) {
    const count = data.count + 1;
    await interaction.update({
      content: `Pong! ×${count}`,
      components: [again({ count })],
    });
  },
});

export default again;
```

Import it where you send a message, and give it its data:

```ts src/commands/ping.ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import { command } from 'chapterjs';
import again from '../components/again';

export default command({
  name: 'ping',
  description: 'Replies with Pong!',
  async run({ interaction }) {
    await interaction.reply({
      content: 'Pong!',
      components: [again({ count: 1 })],
    });
  },
});
```

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.

> [!NOTE]
>
> Everything a button declares, and the types behind it, are in the
> [reference](https://www.chapterjs.org/reference/button.md). The `interaction` of a button is a
> [ComponentInteraction](https://www.chapterjs.org/reference/interaction.md#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.

```ts src/components/ban.ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import { button } from 'chapterjs';

export default button({
  label: 'Ban',
  style: 'danger',
  data: { userId: 'string', days: 'number' },
  async run({ interaction, data, guild }) {
    await guild.ban(data.userId, { deleteMessageSeconds: data.days * 86_400 });
    await interaction.update({ content: 'Banned.', components: [] });
  },
});
```

```ts src/commands/mod/ban.ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import { command } from 'chapterjs';
import ban from '../../components/ban';

export default command({
  name: 'mod ban',
  description: 'Bans a member, after a confirmation',
  options: {
    who: { type: 'user', description: 'Who to ban', required: true },
  },
  async run({ interaction, options }) {
    await interaction.reply({
      content: `Ban ${options.who}?`,
      components: [ban({ userId: options.who.id, days: 1 })],
    });
  },
});
```

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:

```text theme={"theme":{"light":"github-light","dark":"vesper"}}
The data of ban is 97 characters long once encoded, and Discord leaves 96 for it (its id is ban, 3 characters, out of 100). Carry less: an id instead of a name, or a shorter export name.
```

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:

```ts src/components/next.ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import { button } from 'chapterjs';

const next = button({
  label: 'Next',
  emoji: '➡️',
  style: 'secondary',
  data: { page: 'number' },
  async run({ interaction, data }) {
    const page = data.page + 1;
    const last = page >= 5;
    await interaction.update({
      content: `Page ${page} of 5`,
      components: [
        next({ page }, { disabled: last, label: last ? 'The end' : 'Next' }),
      ],
    });
  },
});

export default next;
```

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()`:

```ts src/commands/docs.ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import { command, linkButton } from 'chapterjs';

export default command({
  name: 'docs',
  description: 'Links to the documentation',
  async run({ interaction }) {
    await interaction.reply({
      content: 'Everything is explained here:',
      components: [
        linkButton({ label: 'Documentation', url: 'https://www.chapterjs.org' }),
      ],
    });
  },
});
```

## Answering a click

The `interaction` of a button has everything the interaction of a command has (`reply()`, `defer()`, `edit()`, `followUp()`, `delete()`), plus three things:

| Method | What it does |
| - | - |
| `interaction.update(message)` | Changes the message the button is on. `components: []` removes the buttons. |
| `interaction.deferUpdate()` | Answers with nothing: the message stays as it is, and `update()` can come later. |
| `interaction.showModal(form)` | Opens a [form](https://www.chapterjs.org/components/modals.md). |

`interaction.message` is the message the button is on, as a [Message](https://www.chapterjs.org/reference/message.md).

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":

```text theme={"theme":{"light":"github-light","dark":"vesper"}}
⚠ src/components/silent.ts silent finished without answering: the person sees "This interaction failed". Call interaction.update() or interaction.reply() in run, or interaction.deferUpdate() to change nothing.
```

## 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.

```ts src/components/cancel.ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import { button } from 'chapterjs';

export default button({
  label: 'Cancel',
  style: 'secondary',
  who: 'author',
  async run({ interaction }) {
    await interaction.update({ content: 'Cancelled.', components: [] });
  },
});
```

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](https://www.chapterjs.org/commands.md#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.

> [card] [Select menus](https://www.chapterjs.org/components/selects.md)
>
> Let people pick among options, users, roles or channels.

> [card] [Forms](https://www.chapterjs.org/components/modals.md)
>
> Ask for text and choices in a form that opens in Discord.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.