Using JSX
CommandKit provides first-class JSX support for both TypeScript and JavaScript projects. This allows you to write Discord message components using a familiar and intuitive syntax.
Why Use JSX Instead of Builders?
Using JSX for Discord message components offers several advantages over traditional builder patterns:
Cleaner and More Readable
JSX makes your code significantly more readable by providing a declarative structure that resembles the final output:
// JSX approach - clean and intuitive
const buttons = (
<ActionRow>
<Button
onClick={async (interaction) => {
await interaction.reply({
content: 'The item was not deleted.',
flags: MessageFlags.Ephemeral,
});
}}
style={ButtonStyle.Primary}
>
Cancel
</Button>
<Button
onClick={async (interaction) => {
await interaction.reply({
content: 'The item was deleted successfully.',
flags: MessageFlags.Ephemeral,
});
}}
style={ButtonStyle.Danger}
>
Confirm
</Button>
</ActionRow>
);
await interaction.reply({
content: 'Are you sure you want to delete this item?',
components: [buttons],
});
// Builder approach - more verbose and harder to visualize
const cancel = new ButtonBuilder()
.setCustomId('cancel')
.setLabel('Cancel')
.setStyle(ButtonStyle.Primary);
const confirm = new ButtonBuilder()
.setCustomId('confirm')
.setLabel('Confirm')
.setStyle(ButtonStyle.Danger);
const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
cancel,
confirm,
);
const response = await interaction.reply({
content: 'Are you sure you want to delete this item?',
components: [buttons],
withResponse: true,
});
const confirmation = await response.resource.message.awaitMessageComponent();
if (confirmation.customId === 'confirm') {
await confirmation.reply({
content: 'The item was deleted successfully.',
ephemeral: true,
});
} else if (confirmation.customId === 'cancel') {
await confirmation.reply({
content: 'The item was not deleted.',
ephemeral: true,
});
}
Less Boilerplate
JSX reduces the amount of code you need to write. No more chaining multiple method calls or creating separate builder instances:
- No repetitive
.addComponents()
calls - No need to explicitly create builder instances
- Fewer imports to manage
Improved Developer Experience
- Better IDE support with syntax highlighting and autocompletion
- Easier to spot errors with JSX's structured format
- More intuitive nesting of components
Familiar Syntax
If you've worked with React or other modern frameworks, JSX will feel immediately familiar, reducing the learning curve for building Discord interfaces.
Maintainability
JSX makes your code more maintainable:
- Easier to refactor and modify complex component structures
- Simpler to identify the component hierarchy
- More straightforward to extract reusable components
Setup
CommandKit automatically configures JSX support in your project. If you are doing a manual setup, ensure you have the following in your tsconfig.json
or jsconfig.json
:
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "commandkit"
}
}
Available Components
CommandKit provides JSX versions of all Discord.js builders:
Interactive Components
ActionRow
- Container for interactive componentsButton
- Interactive button componentStringSelectMenu
- Dropdown menu with string optionsChannelSelectMenu
- Dropdown menu for channel selectionRoleSelectMenu
- Dropdown menu for role selectionUserSelectMenu
- Dropdown menu for user selectionMentionableSelectMenu
- Dropdown menu for user/role selectionModal
- Popup form component
Message Components
TextDisplay
- Text content componentContainer
- Message container componentMediaGallery
- Image/video gallery componentSeparator
- Visual separator componentFile
- File attachment component
Example Usage
import { Button, ActionRow } from 'commandkit';
const message = (
<Container>
<TextDisplay>Welcome to our server!</TextDisplay>
<ActionRow>
<Button style="primary">Click me!</Button>
</ActionRow>
</Container>
);
TypeScript Support
CommandKit provides full TypeScript support for JSX components. All components are properly typed, providing autocomplete and type checking:
import { Button, OnButtonKitClick } from 'commandkit';
const handleClick: OnButtonKitClick = async (interaction, context) => {
await interaction.reply('Button clicked!');
context.dispose();
};
const button = <Button onClick={handleClick}>Click me!</Button>;
Custom Components
You can create your own JSX components by defining functions that return Discord.js objects. These components can be used anywhere in your code:
import { Client, ClientOptions } from 'discord.js';
function Bot({ options }: { options: ClientOptions }): Client {
return new Client(options);
}
const client = <Bot options={{ intents: ['Guilds'] }} />;
JSX Fragments
CommandKit fully supports JSX fragments for grouping multiple elements without adding extra nodes:
const elements = (
<>
<TextDisplay>First message</TextDisplay>
<TextDisplay>Second message</TextDisplay>
</>
);
Configuration
The CommandKit compiler automatically configures:
- JSX runtime settings
- TypeScript/JavaScript configuration
- Component type definitions
- Build pipeline optimizations
No additional configuration is needed to use JSX in your CommandKit project.
Best Practices
- Use named imports for specific components:
import { Button, ActionRow } from 'commandkit';
- Keep component trees readable with proper indentation:
const message = (
<Container>
<TextDisplay>Hello!</TextDisplay>
<ActionRow>
<Button>Button 1</Button>
<Button>Button 2</Button>
</ActionRow>
</Container>
);
- Use TypeScript for better type safety and developer experience:
import type { OnButtonKitClick } from 'commandkit';
- Create custom components for reusable Discord.js objects:
function CustomEmbed({
title,
description,
}: {
title: string;
description: string;
}) {
return new EmbedBuilder().setTitle(title).setDescription(description);
}
const embed = <CustomEmbed title="Hello" description="World" />;