Traditional Discord component builders process and transpile complex validation logic at runtime on every single serialization. This creates noticeable bottlenecks in high-scale bots.
@buncord/builders solves this by shifting safety checks to the type level (compile-time). You get a frictionless, zero-dependency, ultra-lightweight library designed natively for Bun that serializes components and is way faster.
- Zero Dependency Footprint: Standard lockfiles shouldn't require dozens of transitives just to build a payload. We ship with zero dependencies.
- Compile-Time Safety: Invalid component structures, overflow lengths, and mismatched options fail while you type, not in production.
- Drop-in Migration: Easily swap
@discordjs/buildersimports out and enjoy near-zero overhead without rewriting your logic. - Next-Gen Component Support: Only support for Discord's latest component.
bun add @buncord/buildersRequirements: Bun ≥ 1.1.0 and TypeScript 5.x
This package is optimized for speed. It runs close to 0ms overhead by using direct manual loops and avoiding heavy validation schemas.
Tip
Performance Boost: With over 7.8x performance (more than 676% faster processing), @buncord/builders eliminates instantiation and serialization bottlenecks entirely, running close to 0ms overhead.
Below are the detailed results comparing 50,000 iterations of component construction and serialization against @discordjs/builders.
Last Benchmarked: June 24, 2026
| Task | @discordjs/builders |
@buncord/builders |
Speed Comparison |
|---|---|---|---|
| Instantiation | ~161.2 ms | ~18.0 ms | 8.9x faster |
| Serialization | ~42.1 ms | ~8.2 ms | 5.1x faster |
| Total | ~203.3 ms | ~26.2 ms | 7.8x faster |
graph LR
classDef msgRoot fill:#5865F2,color:#fff,stroke:none,font-weight:bold,rx:5px,ry:5px
classDef modRoot fill:#FF3B92,color:#fff,stroke:none,font-weight:bold,rx:5px,ry:5px
classDef layout fill:#2b2d2f,color:#fff,stroke:#4f545c,stroke-width:2px
classDef content fill:#202225,color:#dcddde,stroke:#36393f,stroke-width:1px
MSG([Messages]):::msgRoot --> Cont[ContainerBuilder]:::layout
MSG --> RowMsg[ActionRowBuilder]:::layout
Cont --> Sec[SectionBuilder]:::layout
Cont --> RowCont[ActionRowBuilder]:::layout
Cont --> ContContent["MediaGalleryBuilder<br>SeparatorBuilder<br>FileBuilder<br>TextDisplayBuilder"]:::content
Sec --> SecContent["ThumbnailBuilder<br>ButtonBuilder<br>TextDisplayBuilder"]:::content
RowMsg --> RowMsgContent["ButtonBuilder<br>StringSelectMenuBuilder<br>UserSelectMenuBuilder<br>RoleSelectMenuBuilder<br>MentionableSelectMenuBuilder<br>ChannelSelectMenuBuilder"]:::content
RowCont --> RowContContent["ButtonBuilder<br>StringSelectMenuBuilder<br>UserSelectMenuBuilder<br>RoleSelectMenuBuilder<br>MentionableSelectMenuBuilder<br>ChannelSelectMenuBuilder"]:::content
MOD([Modals]):::modRoot --> Lbl[LabelBuilder]:::layout
MOD --> RowMod[ActionRowBuilder]:::layout
MOD --> TxtMod[TextDisplayBuilder]:::content
Lbl --> LblContent["TextInputBuilder<br>RadioGroupBuilder<br>CheckboxGroupBuilder<br>CheckboxBuilder<br>FileUploadBuilder<br>StringSelectMenuBuilder<br>UserSelectMenuBuilder<br>RoleSelectMenuBuilder<br>MentionableSelectMenuBuilder<br>ChannelSelectMenuBuilder"]:::content
RowMod --> RowModContent["TextInputBuilder<br>StringSelectMenuBuilder<br>UserSelectMenuBuilder<br>RoleSelectMenuBuilder<br>MentionableSelectMenuBuilder<br>ChannelSelectMenuBuilder"]:::content
Components V2 messages must be sent with the IS_COMPONENTS_V2 message flag:
import { MessageFlags } from '@buncord/builders';
const flags = MessageFlags.IsComponentsV2;When this flag is set, Discord treats components as the message body. Use TextDisplayBuilder and ContainerBuilder instead of relying on content or embeds.
| Type | ID | Available in |
|---|---|---|
| ActionRow | 1 | Messages · Modals |
| Button | 2 | Messages · Section accessory |
| StringSelect | 3 | Messages · Modals |
| TextInput | 4 | Modals |
| UserSelect | 5 | Messages · Modals |
| RoleSelect | 6 | Messages · Modals |
| MentionableSelect | 7 | Messages · Modals |
| ChannelSelect | 8 | Messages · Modals |
| Section | 9 | Messages |
| TextDisplay | 10 | Messages · Modals |
| Thumbnail | 11 | Messages |
| MediaGallery | 12 | Messages |
| File | 13 | Messages |
| Separator | 14 | Messages |
| Container | 17 | Messages |
| Label | 18 | Modals |
| FileUpload | 19 | Modals |
| RadioGroup | 21 | Modals |
| CheckboxGroup | 22 | Modals |
| Checkbox | 23 | Modals |
All runnable examples live in /exemples:
| Example | Description |
|---|---|
quick-start.ts |
Full Components V2 message payload |
smart-layout.ts |
Auto-packing buttons and select menus into rows |
modals.ts |
Modal with text inputs, radio groups, checkboxes, and file upload |
validation.ts |
Runtime validation and auditTree() diagnostics |
webhook.ts |
Sending a payload to a Discord webhook |
Use toJSON() for eager throwing on violations. Use BaseComponent.auditTree() for non-blocking diagnostics with structured codes, paths, and fix suggestions. See exemples/validation.ts.
import { BaseComponent } from '@buncord/builders';
const warnings = BaseComponent.auditTree(payload);
const issues = BaseComponent.auditTree(payload, { structured: true });
for (const issue of issues) {
console.warn(`[${issue.code}] ${issue.message} → ${issue.fix}`);
}The auditor checks: component limits, duplicate customIds, missing required fields, character length overflows, and mixed ActionRow contents.
bun install # install dependencies
bun test # run tests
bun test --coverage # run tests with coverage
bun run typecheck # run TypeScript type checksSee CONTRIBUTING.md for more details.
Note: The tests, JSDocs, and code comments in this repository were generated by an AI and subsequently reviewed and reworked by a human.