Buttons are interactive components that users can click to trigger actions in your Discord bot.

Buttons in src/components/buttons/ are auto-registered. The customId comes from the file path — you do not call .setCustomId() in the handler file.

File-based routing

File path customId
src/components/buttons/confirm.ts confirm
src/components/buttons/demo/subdemo.ts demo.subdemo
src/components/buttons/shop/buy.ts shop.buy

Nested folders use dot notation, same as slash commands.

Creating a button

import { Button } from "@djs-core/runtime";
import { ButtonStyle } from "discord.js";
export default new Button()
.setLabel("Click me!")
.setStyle(ButtonStyle.Primary)
.run(async (interaction) => {
await interaction.reply("Button clicked!");
});

Button styles

Discord provides several button styles:

  • ButtonStyle.Primary (Blurple)
  • ButtonStyle.Secondary (Gray)
  • ButtonStyle.Success (Green)
  • ButtonStyle.Danger (Red)
  • ButtonStyle.Link (Gray with external link icon)

Using buttons in commands

Import the button and add it to an ActionRowBuilder:

import confirmButton from "../../components/buttons/confirm";
import { Button, Command } from "@djs-core/runtime";
import { ActionRowBuilder } from "discord.js";
export default new Command()
.setDescription("Show a button")
.run(async (interaction) => {
const row = new ActionRowBuilder<Button>().addComponents(confirmButton);
await interaction.reply({
content: "Click the button below!",
components: [row],
});
});

Passing data with .withData()

Declare the data shape with .withData<T>(), then call .setData() when attaching the button to a message:

import { Button } from "@djs-core/runtime";
import { ButtonStyle } from "discord.js";
export default new Button()
.withData<{ userId: string; action: string }>()
.setLabel("Confirm")
.setStyle(ButtonStyle.Danger)
.run(async (interaction, data) => {
await interaction.reply(`Processing ${data.action} for user ${data.userId}`);
});
import deleteButton from "../../components/buttons/delete";
import { Button } from "@djs-core/runtime";
import { ActionRowBuilder } from "discord.js";
const row = new ActionRowBuilder<Button>().addComponents(
deleteButton.setData({ userId: interaction.user.id, action: "delete" }),
);

Data TTL

Optionally expire stored data after a number of seconds:

deleteButton.setData({ userId: "123", action: "delete" }, 60);

Link buttons navigate to external URLs and do not trigger interaction handlers:

import { Button } from "@djs-core/runtime";
import { ButtonStyle } from "discord.js";
export default new Button()
.setLabel("Visit Website")
.setStyle(ButtonStyle.Link)
.setURL("https://example.com");

Link buttons are the only buttons that need .setURL(). Handler buttons get their customId from the file path automatically.