How to Make a Discord Bot: JavaScript discord.js Tutorial for Beginners
A step-by-step JavaScript discord.js tutorial for beginners—learn how to create, deploy, and customize your first Discord bot in under 30 minutes. ✅ No prior bot experience needed!
September 6, 2026 · 1247 views
So you’ve heard about Discord bots—those helpful little digital assistants that moderate servers, play music, log events, or even tell dad jokes—and now you’re ready to build your own. 🤖 Whether you're a coding newbie or a web dev brushing up on Node.js, this how to make a Discord bot guide walks you through the entire process using discord.js, the most popular and well-documented JavaScript library for Discord bot development.
By the end of this tutorial, you’ll have a fully functional, self-hosted bot running on your local machine—and you’ll understand exactly how each piece fits together. No magic, no black boxes—just clear, actionable steps backed by real-world best practices. Let’s dive in!
Prerequisites: What You’ll Need Before Coding
Before writing a single line of JavaScript, ensure you have these tools installed and ready:
- Node.js v18.17+ (LTS recommended) — Download here
- A code editor like VS Code (with ESLint & Prettier extensions recommended) ✨
- A Discord account with a server you can manage (to add your bot)
- Basic familiarity with the terminal/command line and npm
💡 Pro tip: Use node -v and npm -v in your terminal to verify installations. If commands aren’t found, restart your terminal after installing Node.js.
Step 1: Create a New Bot Application on Discord Developer Portal
Discord doesn’t let just any script talk to its API—you need official permission via a bot application.
Here’s how to get one:
- Go to the Discord Developer Portal
- Click "New Application", give it a name (e.g.,
MyFirstBot), then click Create - In the left sidebar, select Bot → Add Bot → Confirm
- Copy the Token (under “Token” → click Copy) — this is your bot’s secret password! 🔑 Never commit it to GitHub or share it publicly.
- Scroll down and toggle "Privileged Gateway Intents":
- ✅
SERVER MEMBERS INTENT - ✅
MESSAGE CONTENT INTENT(required to read message content in v14+)
- ✅
- Still under Bot, scroll to “Privileged Gateway Intents”, and enable both toggles
- Finally, go to OAuth2 → URL Generator, check
botandapplications.commands, then select the scopes and permissions your bot needs (for basic use:Send Messages,Embed Links,Read Message History). Copy the generated OAuth2 URL and open it to add your bot to your test server.
[Image: Discord Developer Portal showing Bot tab with Token and Intent toggles highlighted]
Step 2: Initialize Your Project & Install discord.js
Open your terminal in an empty folder (e.g., my-discord-bot) and run:
npm init -y
npm install discord.js@14.16.3
npm install dotenv
✅ Why dotenv? It lets you safely store your bot token outside your code (more on that soon).
Now create two essential files:
index.js— your bot’s main entry point.env— a hidden config file to store secrets
In .env, add:
TOKEN=your_bot_token_here_from_step_1
CLIENT_ID=your_app_id_from_developer_portal
⚠️ Replace your_bot_token_here_from_step_1 with the actual token you copied earlier—but never push .env to GitHub! Add it to your .gitignore:
.env
node_modules/
Step 3: Write Your First Bot Script (index.js)
Here’s a minimal but production-ready starter bot using discord.js v14 (ES Modules syntax):
import { REST, Routes } from 'discord.js';
import { Client, GatewayIntentBits, Collection } from 'discord.js';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
import 'dotenv/config';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Initialize client
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
GatewayIntentBits.GuildMembers,
],
});
client.commands = new Collection();
// Load command files
const commandsPath = path.join(__dirname, 'commands');
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
for (const file of commandFiles) {
const filePath = path.join(commandsPath, file);
const command = await import(filePath);
if ('data' in command && 'execute' in command) {
client.commands.set(command.data.name, command);
}
}
// Event: Ready
client.once('ready', () => {
console.log(`✅ ${client.user.tag} is online and ready!`);
});
// Event: Interaction Create (slash commands)
client.on('interactionCreate', async interaction => {
if (!interaction.isChatInputCommand()) return;
const command = client.commands.get(interaction.commandName);
if (!command) return;
try {
await command.execute(interaction);
} catch (error) {
console.error(error);
await interaction.reply({
content: '❌ There was an error while executing this command!',
ephemeral: true,
});
}
});
// Login
client.login(process.env.TOKEN);
📌 This version uses modern ES modules and handles slash commands properly—no deprecated message event reliance. It also prepares your bot for scalability (like loading commands from separate files).
Step 4: Add Your First Slash Command
Let’s make /ping—a classic “hello world” for bots.
Create a commands folder in your project root, then add ping.js inside it:
import { SlashCommandBuilder } from 'discord.js';
export const data = new SlashCommandBuilder()
.setName('ping')
.setDescription('Replies with pong!');
export async function execute(interaction) {
await interaction.reply('🏓 Pong!');
}
That’s it! But wait—your bot won’t know this command exists until you register it with Discord’s API.
Step 5: Register Slash Commands
Discord requires you to explicitly register slash commands before they appear. Create deploy-commands.js in your project root:
import { REST, Routes } from 'discord.js';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { readdirSync } from 'fs';
import 'dotenv/config';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const commands = [];
const commandsPath = join(__dirname, 'commands');
const commandFiles = readdirSync(commandsPath).filter(file => file.endsWith('.js'));
for (const file of commandFiles) {
const filePath = join(commandsPath, file);
const command = await import(filePath);
if ('data' in command && 'execute' in command) {
commands.push(command.data.toJSON());
}
}
const rest = new REST({ version: '10' }).setToken(process.env.TOKEN);
(async () => {
try {
console.log(`⏳ Registering ${commands.length} slash command(s)...`);
// For global commands (available across all servers)
await rest.put(
Routes.applicationCommands(process.env.CLIENT_ID),
{ body: commands },
);
console.log(`✅ Successfully registered ${commands.length} command(s)!`);
} catch (error) {
console.error(error);
}
})();
Then run it once:
node deploy-commands.js
💡 Pro tip: Use Routes.applicationGuildCommands(CLIENT_ID, GUILD_ID) instead if you want commands only in one test server (faster iteration, no 1-hour global cache delay).
Step 6: Run & Test Your Bot
Start your bot:
node index.js
You should see ✅ MyFirstBot#1234 is online and ready! in your terminal.
Now head to your Discord server → type /ping → hit Enter → 🎉 you’ll get 🏓 Pong!
If nothing happens:
- Double-check intents are enabled in the Developer Portal
- Verify your bot has
Send Messagespermission in the channel - Ensure you deployed commands after adding the bot to the server
- Check console logs for errors
Quick Tips for Real-World Bot Development
- Always validate inputs: Use
.options.getString('name')withrequired: truein command builders, and guard against undefined values. - Use cooldowns wisely: Prevent spam with
CooldownManageror simple timestamp tracking. - Log everything important: Add
client.on('error', console.error)andclient.on('warn', console.warn). - Store config separately: Use
config.jsonor environment variables—not hardcoded strings. - Handle errors gracefully: Wrap
interaction.reply()in try/catch blocks, especially when fetching external APIs. - Respect rate limits:
discord.jsauto-handles most, but avoid rapid bulk actions (e.g., mass-kicking users). - Keep dependencies updated: Run
npm outdatedmonthly and patch security vulnerabilities withnpm audit fix.
Pro Tips: Level Up Your Bot 🚀
✅ Add logging to a channel: Create a #bot-logs channel and send embeds there for joins, errors, or command usage.
✅ Use buttons & modals: Modern bots go beyond /commands. Try interaction.showModal() for forms or interaction.reply({ components: [...] }) for interactive UIs.
✅ Deploy to cloud (free tier): Use Railway, Render, or Fly.io to keep your bot online 24/7—no need to leave your laptop running! 🌐
✅ Version control + CI: Initialize Git early. Use GitHub Actions to auto-deploy on main push—great for team projects.
✅ Explore DiscordCraft: Looking for battle-tested boilerplates, command templates, or modular architecture patterns? DiscordCraft offers open-source starter kits, middleware examples, and community-reviewed patterns—all designed for discord.js v14+. It’s like a cheat sheet written by seasoned bot devs. 🛠️
FAQ: Common Beginner Questions
Q: Why does my bot not respond to /commands even after deploying?
A: Slash commands take up to 1 hour to appear globally. For instant testing, deploy to a specific guild using Routes.applicationGuildCommands(clientId, guildId) and restart your bot.
Q: Can I run multiple bots from one codebase?
A: Yes—use separate .env files (e.g., .env.production, .env.development) and load conditionally with dotenv.config({ path: '.env.production' }).
Q: Is discord.js safe for production?
A: Absolutely—it’s actively maintained, TypeScript-first, and used by top-tier bots like MEE6 and Dyno. Just follow security best practices (token safety, input sanitization, rate limiting).
Q: Do I need TypeScript?
A: Not required—but highly recommended. discord.js ships with full TS support, and IDE autocompletion + compile-time checks save hours of debugging. Start with npm install -D typescript @types/node and a tsconfig.json.
Wrapping Up: Your Bot Journey Starts Now
You’ve just built, deployed, and tested your first Discord bot using modern discord.js practices. 🎉 That /ping command? It’s the seed of something much bigger—a moderation assistant, a leveling system, a ticketing interface, or even your own AI-powered chatbot.
Remember: every expert bot developer started exactly where you are now—with a blank index.js file and a healthy dose of curiosity. Keep iterating, break things, read the official discord.js guide, join the discord.js server, and don’t hesitate to peek at open-source repos for inspiration.
Happy coding—and may your uptime be 100%, your slash commands always register, and your error logs stay beautifully empty. 🌟
Need a head start? Check out DiscordCraft’s free discord.js v14 starter template — pre-configured with ESLint, Prettier, command loader, and deployment scripts. Because building shouldn’t mean reinventing the wheel. 🛠️