Skip to main content
Version: 1.x

Radio

CommandKit provides RadioGroup and RadioGroupOption for building a single-choice input (exactly one option) using Discord's component builders.

Basic usage​

Use RadioGroup when you want the user to pick exactly one item from a list:

src/app/commands/radio-group.tsx
import {
type ChatInputCommand,
type OnModalKitSubmit,
Modal,
Label,
RadioGroup,
RadioGroupOption,
} from 'commandkit';
import { MessageFlags } from 'discord.js';

const handleSubmit: OnModalKitSubmit = async (interaction, context) => {
// returns the selected option's value
const plan = interaction.fields.getRadioGroup('plan', true);

await interaction.reply({
content: `Selected plan: ${plan}`,
flags: MessageFlags.Ephemeral,
});

// Clean up the modal context
context.dispose();
};

export const chatInput: ChatInputCommand = async ({ interaction }) => {
const modal = (
<Modal title="Choose a plan" onSubmit={handleSubmit}>
<Label label="Plan" description="Select one option">
<RadioGroup customId="plan" required>
<RadioGroupOption
label="Free"
value="free"
description="Basic features"
/>
<RadioGroupOption
label="Pro"
value="pro"
description="Extra features"
/>
<RadioGroupOption
label="Enterprise"
value="enterprise"
description="Custom agreement"
/>
</RadioGroup>
</Label>
</Modal>
);

await interaction.showModal(modal);
};

Descriptions​

To add more context to each option, use the description prop on RadioGroupOption. This will render a smaller text below the option label, giving users more information to make their choice:

src/app/commands/radio-descriptions.tsx
import {
type ChatInputCommand,
type OnModalKitSubmit,
Modal,
Label,
RadioGroup,
RadioGroupOption,
} from 'commandkit';
import { MessageFlags } from 'discord.js';

const handleSubmit: OnModalKitSubmit = async (interaction, context) => {
const choice = interaction.fields.getRadioGroup('choice', true);

await interaction.reply({
content: `You chose: ${choice}`,
flags: MessageFlags.Ephemeral,
});

context.dispose();
};

export const chatInput: ChatInputCommand = async ({ interaction }) => {
const modal = (
<Modal title="Feedback" onSubmit={handleSubmit}>
<Label label="How was your experience?">
<RadioGroup customId="choice" required>
<RadioGroupOption
label="Good"
value="good"
description="Had a great experience"
/>
<RadioGroupOption
label="Okay"
value="okay"
description="It was fine, nothing special"
/>
<RadioGroupOption
label="Bad"
value="bad"
description="Had a poor experience"
/>
</RadioGroup>
</Label>
</Modal>
);

await interaction.showModal(modal);
};

Default selection​

Use default on RadioGroupOption to pre-select an option:

src/app/commands/default-radio.tsx
import {
type ChatInputCommand,
type OnModalKitSubmit,
Modal,
Label,
RadioGroup,
RadioGroupOption,
} from 'commandkit';
import { MessageFlags } from 'discord.js';

const handleSubmit: OnModalKitSubmit = async (interaction, context) => {
const frequency = interaction.fields.getRadioGroup('frequency', true);

await interaction.reply({
content: `Notification frequency: ${frequency}`,
flags: MessageFlags.Ephemeral,
});

context.dispose();
};

export const chatInput: ChatInputCommand = async ({ interaction }) => {
const modal = (
<Modal title="Notifications" onSubmit={handleSubmit}>
<Label label="How often should we notify you?">
<RadioGroup customId="frequency" required>
<RadioGroupOption label="Daily" value="daily" />
<RadioGroupOption label="Weekly" value="weekly" default />
<RadioGroupOption label="Never" value="never" />
</RadioGroup>
</Label>
</Modal>
);

await interaction.showModal(modal);
};

Required vs optional​

If required is set, users must pick an option before submitting the modal. When required is not set (or is false), you can read the value as string | null:

src/app/commands/optional-radio.tsx
import {
type ChatInputCommand,
type OnModalKitSubmit,
Modal,
Label,
RadioGroup,
RadioGroupOption,
} from 'commandkit';
import { MessageFlags } from 'discord.js';

const handleSubmit: OnModalKitSubmit = async (interaction, context) => {
const choice = interaction.fields.getRadioGroup('choice');

await interaction.reply({
content: `Choice: ${choice ?? 'skipped'}`,
flags: MessageFlags.Ephemeral,
});

context.dispose();
};

export const chatInput: ChatInputCommand = async ({ interaction }) => {
const modal = (
<Modal title="Quick question" onSubmit={handleSubmit}>
<Label label="Pick one (optional)">
<RadioGroup customId="choice" required={false}>
<RadioGroupOption label="Yes" value="yes" />
<RadioGroupOption label="No" value="no" />
</RadioGroup>
</Label>
</Modal>
);

await interaction.showModal(modal);
};