discord-utils-kit
v0.1.5
Published
Lightweight embed presets, slash command helpers, and cooldown manager for discord.js bots
Maintainers
Readme
discord-utils-kit
Lightweight, typed utilities for discord.js bots: embed presets, a slash command registry, and a cooldown manager. Zero bloat, no bundled discord.js — just the parts you'd otherwise rewrite in every project.
Install
npm install discord-utils-kit discord.jsdiscord.js is a peer dependency — you bring your own version (v14+).
Embeds
import { successEmbed, errorEmbed, warningEmbed, infoEmbed } from "discord-utils-kit";
await interaction.reply({
embeds: [successEmbed({ description: "Role assigned!" })],
});
await interaction.reply({
embeds: [errorEmbed({ description: "You don't have permission to do that." })],
});Each preset (successEmbed, errorEmbed, warningEmbed, infoEmbed) accepts the same options:
{
title?: string; // overrides the default preset title
description?: string;
footer?: string;
fields?: { name: string; value: string; inline?: boolean }[];
timestamp?: boolean;
}Paginated embeds
import { paginatedEmbeds } from "discord-utils-kit";
const pages = paginatedEmbeds({
title: "Leaderboard",
pages: ["Page 1 content...", "Page 2 content...", "Page 3 content..."],
});
// pages[0], pages[1], pages[2] — wire up with buttons yourselfSlash commands
import { CommandRegistry } from "discord-utils-kit";
import { SlashCommandBuilder } from "discord.js";
const registry = new CommandRegistry();
registry.add({
data: new SlashCommandBuilder()
.setName("ping")
.setDescription("Replies with pong"),
execute: async (interaction) => {
await interaction.reply("Pong!");
},
});
// Register with Discord's REST API
await rest.put(Routes.applicationCommands(clientId), { body: registry.toJSON() });
// In your interactionCreate listener
client.on("interactionCreate", async (interaction) => {
if (interaction.isChatInputCommand()) {
await registry.handleInteraction(interaction);
}
});Cooldowns
import { CooldownManager } from "discord-utils-kit";
const cooldowns = new CooldownManager(10); // default: 10 seconds
// inside a command handler
const remaining = cooldowns.check(`ping:${interaction.user.id}`);
if (remaining > 0) {
return interaction.reply({
embeds: [errorEmbed({ description: `Slow down! Try again in ${remaining}s.` })],
ephemeral: true,
});
}Components v2 containers
Discord's newer Components v2 system (ContainerBuilder, TextDisplayBuilder, SectionBuilder, etc.) is powerful but verbose to hand-write. customContainer lets you describe one with a plain array of blocks instead:
import { containerMessage } from "discord-utils-kit";
await interaction.reply(
containerMessage({
accentColor: 0x5865f2,
blocks: [
{ type: "text", content: "# Welcome!" },
{ type: "separator" },
{
type: "section",
text: "Pick a role below",
button: { customId: "role_pick", label: "Choose" },
},
{
type: "buttons",
buttons: [
{ customId: "confirm", label: "Confirm" },
{ customId: "cancel", label: "Cancel" },
],
},
],
})
);containerMessage() returns { components, flags } ready to spread into interaction.reply() or channel.send() — it automatically sets the required MessageFlags.IsComponentsV2 flag for you.
If you want the raw ContainerBuilder instead (e.g. to combine with other components manually), use customContainer() directly:
import { customContainer } from "discord-utils-kit";
import { MessageFlags } from "discord.js";
const container = customContainer({
blocks: [{ type: "text", content: "Just a container" }],
});
await channel.send({ components: [container], flags: MessageFlags.IsComponentsV2 });Supported block types:
| type | fields | maps to |
|---|---|---|
| text | content | TextDisplayBuilder |
| separator | divider?, spacing?: "small" \| "large" | SeparatorBuilder |
| section | text (string or up to 3), button?: { customId, label, style? } | SectionBuilder |
| gallery | items: { url, description?, spoiler? }[] | MediaGalleryBuilder |
| buttons | buttons: { customId, label, style? }[] | ActionRowBuilder<ButtonBuilder> |
Note: Components v2 messages can't include content, embeds, polls, or stickers — Discord treats them as a separate rendering mode. Requires discord.js v14.19+.
Templates
A ready-made welcome bot is included under the templates subpath. It reads a JSON configuration for the Components v2 container and can optionally auto-assign a role when a new member joins.
const { welcomebot } = require("discord-utils-kit/templates");
welcomebot({
token: process.env.DISCORD_TOKEN,
welcomeChannelId: "123456789012345678",
autoGiveRole: true,
roleId: "987654321098765432",
configPath: "data/welcome.json",
});If you omit token, welcomeChannelId, and roleId, the bot prompts you in the terminal. Note: templates no longer ship with a built-in default data/welcome.json. You must create the JSON config file yourself and either:
- pass its path via
configPath(e.g.configPath: "data/welcome.json"), or - pass a parsed config object via
config(a CustomContainerOptions object) in the options.
If the configured file does not exist or contains invalid JSON, the template will throw a clear error explaining the required action.
Changelog
v0.1.4 - 2026-08-13
- Templates: removed bundled
data/welcome.json. The welcome template now requires an explicit config file (viaconfigPath) or aconfigobject. Missing/invalid config now throws a clear error so the developer provides the correct file. - README: updated to explain the new template behavior.
v0.1.3 - 2026-08-13
- Fix: corrected welcome config example (accentColor parsing) and minor documentation fixes.
Why this package
Every Discord bot ends up rewriting the same three things: colored embed presets, a command registry, and a cooldown map. This package gives you all three, fully typed, with no dependency bloat — discord.js stays a peer dependency so you control the version.
License
MIT
