Welcome new members (discord.js)

Greet everyone who joins with an embed in your welcome channel — and turn on the one privileged intent that makes join events reach your bot at all.

Skip the setup This guide has a one-click starter template that installs everything below onto your server.

This recipe posts a welcome message every time someone joins your server: a friendly embed with the new member's name, avatar, and their member number. The catch — and the whole reason this needs its own guide — is that join events don't reach your bot until you flip one privileged switch in the Developer Portal.

At a glance
You need a working bot from Host a discord.js bot, and a channel to post welcomes in
Plan Free or premium
Time About fifteen minutes

Start from the discord.js bot guide if you haven't — you need a bot with your token in .env already running.

Turn on the Server Members intent first

Your bot can't be told about new members unless Discord is allowed to send it member events, and that permission is a privileged intent you toggle by hand.

⚠️ Heads up — this is a privileged intent. In the Discord Developer Portal, open your application → Bot → scroll to Privileged Gateway Intents → turn on Server Members Intent → save. The code below requests GatewayIntentBits.GuildMembers; if that toggle is off, the bot crashes at login with Used disallowed intents instead of starting. On servers over 100 members Discord also asks you to verify the bot before it will grant this intent.

The code

The complete index.js. It reads the target channel's ID from .env, so you don't hard-code it:

require('dotenv').config();
const {
  Client, Events, GatewayIntentBits,
  EmbedBuilder,
} = require('discord.js');

const client = new Client({
  intents: [
    GatewayIntentBits.Guilds,
    GatewayIntentBits.GuildMembers,
  ],
});

client.once(Events.ClientReady, (c) => {
  console.log(`Listening as ${c.user.tag}`);
});

client.on(Events.GuildMemberAdd, async (member) => {
  const channel = member.guild.channels.cache.get(process.env.WELCOME_CHANNEL_ID);
  if (!channel) return;

  const embed = new EmbedBuilder()
    .setColor(0x57f287)
    .setTitle('👋 Welcome!')
    .setDescription(`Hey ${member}, welcome to **${member.guild.name}**!`)
    .setThumbnail(member.user.displayAvatarURL())
    .addFields(
      { name: 'Member', value: `${member.user.tag}`, inline: true },
      { name: 'You are member #', value: `${member.guild.memberCount}`, inline: true },
    )
    .setTimestamp();

  await channel.send({ content: `${member}`, embeds: [embed] });
});

client.login(process.env.DISCORD_TOKEN);

Then add the channel's ID to your .env, next to the token:

DISCORD_TOKEN=your-token
WELCOME_CHANNEL_ID=123456789012345678

To get a channel's ID, enable Developer Mode in Discord (User Settings → Advanced), then right-click the welcome channel and choose Copy Channel ID. Save .env and restart.

The parts that matter

  • GatewayIntentBits.GuildMembers in the intents array is what asks Discord to send GuildMemberAdd events. It only works because you enabled the matching toggle above — the two must agree.
  • Events.GuildMemberAdd fires once per join. Its member argument is the person who joined, carrying their guild (member.guild) and user (member.user).
  • member.guild.channels.cache.get(process.env.WELCOME_CHANNEL_ID) looks up the channel to post in. The if (!channel) return guard means a wrong or deleted ID fails quietly instead of crashing.
  • ${member} in a string becomes a live @mention that pings the new member. Putting it in content (outside the embed) is what actually notifies them — mentions inside an embed don't ping.
  • member.guild.memberCount is the running total, so "You are member #1,024" comes for free.

The embed itself is standard — Embeds deep dive covers every field it can hold.

Making it yours

  • Post in the system channel automatically. Swap the lookup for member.guild.systemChannel to use whatever channel Discord already flags for system messages — no ID needed.
  • DM the newcomer instead. await member.send('Welcome!') messages them directly. Wrap it in try/catch: people who block DMs from server members will throw an error.
  • Say goodbye too. Add an Events.GuildMemberRemove handler for a leave message — it uses the same member object and needs the same intent.
  • Auto-assign a role while you're here. Since you've already enabled the Server Members intent, Give every new member a role is a two-line addition to this same handler.

Troubleshooting

  • Used disallowed intents at startup — the Server Members Intent toggle is off in the Developer Portal. Turn it on (see the callout) and restart. See Bot appears offline.
  • Bot starts fine but never welcomes anyone — either the intent toggle is off (no crash, just silence on older setups) or WELCOME_CHANNEL_ID is wrong. Confirm the ID with Copy Channel ID and that the bot can see and post in that channel.
  • The welcome posts but doesn't ping the new member — the mention has to be in content, not inside the embed. The code puts ${member} in content for exactly this reason.
  • Nothing in a channel the bot can't access — check the bot's role has View Channel and Send Messages on the welcome channel.

Next steps

Was this guide helpful?