Skip to main content

Buttons

Buttons are defined via .setButtons() in embeds mode, or via the button() helper in layout mode. Each button is a config object with a label, style, and an action or link.

Button properties​

PropertyTypeRequiredDescription
labelstringYesText displayed on the button
styleButtonStyleYesButton color (see below)
actionActionConditionalCallback executed on click
opensModalboolean | stringConditionalTrigger a modal instead of running an action
urlstringConditionalURL for link buttons — requires ButtonStyle.Link
disabledbooleanNoRenders the button as unclickable
emojistringNoEmoji displayed on the button
idstringNoCustom component ID — auto-assigned if omitted
fixedPosition'start' | 'end'NoPins button across pagination pages (embeds mode only)

action or opensModal must be present unless disabled: true or the button is a link button.

Button styles​

ButtonStyle is imported from discord.js:

import { ButtonStyle } from 'discord.js';
StyleColorUse
ButtonStyle.PrimaryBluePrimary action
ButtonStyle.SecondaryGreySecondary or neutral action
ButtonStyle.SuccessGreenConfirm, save, submit
ButtonStyle.DangerRedDelete, remove, destructive action
ButtonStyle.LinkNo backgroundURL link — no interaction sent to bot

Basic usage​

.setButtons((ctx) => [
{
label: 'Confirm',
style: ButtonStyle.Success,
action: async (ctx) => {
await db.save(ctx.state.get('item'));
await ctx.goTo('confirmation');
},
},
{
label: 'Cancel',
style: ButtonStyle.Danger,
action: closeMenu(),
},
])

Link buttons navigate the user to a URL without sending an interaction to your bot. They require ButtonStyle.Link and a url — action and opensModal cannot be set alongside url.

{
label: 'View on GitHub',
style: ButtonStyle.Link,
url: 'https://github.com/flowcord-dev/flowcord-core',
}

Disabled buttons​

Set disabled: true to render a button as greyed out and unclickable. Useful for showing unavailable options conditionally:

{
label: 'Purchase',
style: ButtonStyle.Success,
disabled: ctx.state.get('coins') < item.price,
action: async (ctx) => { /* ... */ },
}

A disabled button does not require action or opensModal — the validator skips that check when disabled: true. You can omit both if the button is purely decorative (e.g., a greyed-out label for context).

To open a modal when a button is clicked, set opensModal instead of action. When both are set, opensModal takes precedence.

// Single modal — opensModal: true
{
label: 'Fill Out Form',
style: ButtonStyle.Primary,
opensModal: true,
}

// Multi-modal — opensModal: 'modal-id'
{
label: 'Edit Details',
style: ButtonStyle.Secondary,
opensModal: 'edit-details',
}

See Modals for how to define the modal the button triggers.

Fixed position buttons (pagination)​

Button pagination activates automatically when the total number of buttons would exceed Discord's component limits. FlowCord splits the overflow into pages and injects Next and Previous navigation buttons — no additional configuration required.

When pagination is active, all buttons cycle through pages by default. fixedPosition pins a button to the start or end of every page so it's always visible regardless of which page is active.

.setButtons((ctx) => [
{
label: '← Back to Menu',
style: ButtonStyle.Secondary,
fixedPosition: 'start', // Always visible, doesn't paginate
action: goBack(),
},
// ... paginated buttons in between ...
{
label: 'Done',
style: ButtonStyle.Primary,
fixedPosition: 'end', // Always visible at the end
action: closeMenu(),
},
], { pagination: { perPage: 5 } })

Fixed buttons count toward the total button limit per page.