Fluent API and Component Architecture

Building UI or Discord interactions using classic discord.js often forces you into excessive "Builder" chaining (ActionRowBuilder, ButtonBuilder, EmbedBuilder). The Disapp packages/core provides a much smoother, single-line architecture.

Components V2 (Modern)

We simplify the extensive button and menu construction process with the Disapp V2 Components structure. There's no longer a need to spread your row logic across massive file blocks.

Constructing Messages with v2()

You can design interactive messages seamlessly using the v2() builder provided by the core framework.

import { v2 } from "@disapp/core";
import { ButtonStyle } from "discord.js";

const message = v2()
  .text("# Do you accept the agreement?")
  .separator()
  .buttons(
    {
      id: "accept_terms",
      label: "Accept Terms",
      style: ButtonStyle.Success,
      emoji: "✅",
      onClick: async (i) => {
        await i.reply("Terms accepted!");
      },
    },
    {
      id: "decline",
      label: "Decline",
      style: ButtonStyle.Danger,
      onClick: async (i) => {
        await i.reply("Declined.");
      },
    },
  )
  .accentColor(0x5865f2)
  .build();

await interaction.reply(message);

v2() Builder Methods

MethodDescriptionReturns
text(content)Add text display componentthis
separator(divider?, spacing?)Add separator componentthis
buttons(...buttons)Add button row with onClick handlersthis
select(id, placeholder, options, onChange?)Add select menu with onChange handlerthis
accentColor(color)Set container accent colorthis
enableAutoChunking(options?)Enable auto-chunking for long textthis
build()Build final message payloadobject

Select Menus

const message = v2()
  .text("# Choose an option")
  .select(
    "my_select",
    "Select something",
    [
      { label: "Option A", value: "a", emoji: "🅰️" },
      { label: "Option B", value: "b", emoji: "🅱️" },
    ],
    async (i) => {
      const selected = i.values[0];
      await i.reply(`You selected: ${selected}`);
    },
  )
  .build();

Message Automation (Message Chunker)

Discord enforces a strict character limit (maximum 2000 characters). If you exceed this limit when logging or sending long texts, a DiscordAPIError is thrown.

Disapp contains a built-in AutoChunker to bypass these limitations automatically.

Auto-Chunking with v2()

const message = v2()
  .enableAutoChunking({
    maxLength: 2000,
    preserveCodeBlocks: true,
  })
  .text(veryLongText)
  .build();

Manual Chunking

import { AutoChunker } from "@disapp/core";

const longText = "..."; // 5000 characters
const chunks = AutoChunker.autoChunk(longText, {
  maxLength: 2000,
  preserveCodeBlocks: true,
});

for (const chunk of chunks) {
  await interaction.channel.send(chunk);
}

Components V1 (Legacy)

For traditional Discord.js components, use the msg() builder:

import { msg, embed, ButtonStyle } from "@disapp/core";

const message = msg()
  .setContent("Welcome!")
  .buttons(
    { label: "Accept", id: "accept", style: ButtonStyle.Success },
    { label: "Decline", id: "decline", style: ButtonStyle.Danger },
  )
  .addEmbed(
    embed()
      .setTitle("Terms of Service")
      .setDescription("Please read and accept our terms")
      .setColor(0x5865f2),
  )
  .build();

await interaction.reply(message);

Shortcuts

Button Shortcuts

import { confirm, yesNo, primary, danger } from "@disapp/core";

confirm("confirm_id", "cancel_id");
yesNo("yes_id", "no_id");
primary("Edit", "edit_id");
danger("Delete", "delete_id");

Embed Shortcuts

import {
  successEmbed,
  errorEmbed,
  warningEmbed,
  infoEmbed,
} from "@disapp/core";

successEmbed("Success!", "Operation completed");
errorEmbed("Error!", "Something went wrong");
warningEmbed("Warning!", "Be careful");
infoEmbed("Info", "Here is some information");

Complete Example

import { Command, v2 } from "@disapp/core";
import { SlashCommandBuilder, ButtonStyle } from "discord.js";

export default class MenuCommand extends Command {
  constructor() {
    super({
      name: "menu",
      description: "Show interactive menu",
      data: new SlashCommandBuilder()
        .setName("menu")
        .setDescription("Show interactive menu"),
      execute: async () => {},
    });
  }

  async execute(interaction: any) {
    const message = v2()
      .text("# Main Menu")
      .separator()
      .text("Choose an option below:")
      .buttons(
        {
          id: "option1",
          label: "Option 1",
          style: ButtonStyle.Primary,
          emoji: "1️⃣",
          onClick: async (i) => {
            await i.reply("You selected Option 1!");
          },
        },
        {
          id: "option2",
          label: "Option 2",
          style: ButtonStyle.Success,
          emoji: "2️⃣",
          onClick: async (i) => {
            await i.reply("You selected Option 2!");
          },
        },
      )
      .select(
        "action",
        "Choose an action",
        [
          { label: "View Profile", value: "profile", emoji: "👤" },
          { label: "Settings", value: "settings", emoji: "⚙️" },
          { label: "Help", value: "help", emoji: "❓" },
        ],
        async (i) => {
          const action = i.values[0];
          await i.reply(`You chose: ${action}`);
        },
      )
      .accentColor(0x5865f2)
      .build();

    await interaction.reply(message);
  }
}

Learn More