Intermédiaire ⏱ 25 min de mise en œuvre Mis à jour le 11 août 2026

Créer des embeds et des boutons interactifs pour son bot Discord

Les messages bruts sont limités. Les embeds structurent l'information, les boutons et menus la rendent interactive. Voici comment construire des messages riches et fiables avec discord.js v14.

Un message Discord brut suffit pour une réponse courte, mais il montre vite ses limites dès qu’il faut présenter une commande, une fiche utilisateur ou plusieurs informations structurées. Les embeds permettent de créer des messages plus professionnels et lisibles grâce à un titre, une couleur, des champs, des images et un pied de page.

Les composants interactifs ajoutent une véritable interface au bot. Avec des boutons et des menus déroulants, vous pouvez construire un menu de rôles, demander une confirmation, naviguer entre plusieurs pages ou déclencher une action sans demander à l’utilisateur de saisir une nouvelle commande.

Créer un embed avec EmbedBuilder

Dans discord.js v14, un embed se construit avec la classe EmbedBuilder. Chaque méthode configure une partie du bloc riche :

  • setTitle définit le titre.
  • setDescription ajoute le contenu principal.
  • setColor affiche une couleur sur le côté gauche.
  • addFields organise les informations en champs.
  • setThumbnail ajoute une petite image.
  • setImage affiche une image large.
  • setFooter configure le pied de page.
  • setTimestamp ajoute une date.
  • setAuthor indique un auteur avec un nom et éventuellement une icône.

Voici un exemple de fiche serveur :

const { EmbedBuilder } = require('discord.js');

const serverEmbed = new EmbedBuilder()
  .setColor(0x5865f2)
  .setAuthor({
    name: 'TalCloud Bot',
    iconURL: 'https://example.com/bot-icon.png'
  })
  .setTitle('État du serveur')
  .setDescription('Toutes les informations importantes sont disponibles ci-dessous.')
  .setThumbnail('https://example.com/server-icon.png')
  .addFields(
    {
      name: 'Statut',
      value: 'En ligne',
      inline: true
    },
    {
      name: 'Latence',
      value: '42 ms',
      inline: true
    },
    {
      name: 'Région',
      value: 'France',
      inline: true
    }
  )
  .setImage('https://example.com/banner.png')
  .setFooter({
    text: 'Dernière vérification'
  })
  .setTimestamp();

await interaction.reply({
  embeds: [serverEmbed]
});

La propriété inline permet d’afficher plusieurs champs sur une même ligne lorsque l’espace disponible le permet. Elle est utile pour les statistiques courtes, mais moins adaptée aux textes longs.

Un embed peut être envoyé avec interaction.reply, interaction.editReply, message.reply ou une méthode similaire. Il faut le placer dans le tableau embeds, même si le message n’en contient qu’un seul.

Respecter les limites des embeds Discord

Discord impose plusieurs limites qu’il faut intégrer dès la conception :

  • Environ 256 caractères pour le titre.
  • Environ 4 096 caractères pour la description.
  • Un maximum de 25 champs par embed.
  • Environ 6 000 caractères au total pour l’ensemble du contenu textuel d’un embed.
  • Un maximum de 10 embeds par message.

Chaque nom et chaque valeur de champ possède également sa propre limite. Il est donc préférable de découper les contenus volumineux ou d’utiliser une pagination.

Ne comptez pas sur Discord pour présenter proprement un embed trop chargé. Une fiche concise, avec quelques champs clairement nommés, sera généralement plus facile à consulter qu’un bloc rempli jusqu’aux limites techniques.

Ajouter des boutons interactifs

Les boutons sont créés avec ButtonBuilder, puis placés dans une ActionRowBuilder. Une ligne peut contenir jusqu’à cinq boutons et un message peut comporter jusqu’à cinq lignes de composants.

discord.js propose cinq styles principaux :

  • Primary pour une action principale.
  • Secondary pour une action neutre.
  • Success pour une validation.
  • Danger pour une suppression ou une action sensible.
  • Link pour ouvrir une URL.

Tous les boutons interactifs utilisent un customId. Ce dernier permet au bot de reconnaître l’action demandée. Un bouton Link constitue une exception : il utilise une URL, ne possède pas de customId et ne déclenche aucune interaction côté bot.

L’exemple suivant crée une confirmation, un menu déroulant et un gestionnaire global :

const {
  ActionRowBuilder,
  ButtonBuilder,
  ButtonStyle,
  StringSelectMenuBuilder,
  MessageFlags
} = require('discord.js');

const confirmationRow = new ActionRowBuilder().addComponents(
  new ButtonBuilder()
    .setCustomId('ticket:confirm')
    .setLabel('Confirmer')
    .setStyle(ButtonStyle.Success),
  new ButtonBuilder()
    .setCustomId('ticket:cancel')
    .setLabel('Annuler')
    .setStyle(ButtonStyle.Danger),
  new ButtonBuilder()
    .setLabel('Documentation')
    .setURL('https://example.com/docs')
    .setStyle(ButtonStyle.Link)
);

const roleRow = new ActionRowBuilder().addComponents(
  new StringSelectMenuBuilder()
    .setCustomId('role:select')
    .setPlaceholder('Choisissez un rôle')
    .addOptions(
      {
        label: 'Développeur',
        value: 'developer',
        description: 'Recevoir le rôle Développeur'
      },
      {
        label: 'Notifications',
        value: 'notifications',
        description: 'Recevoir les annonces'
      }
    )
);

await interaction.reply({
  content: 'Configurez votre demande :',
  components: [confirmationRow, roleRow]
});

client.on('interactionCreate', async interaction => {
  if (interaction.isButton()) {
    if (interaction.customId === 'ticket:confirm') {
      await interaction.update({
        content: 'Demande confirmée.',
        components: []
      });
      return;
    }

    if (interaction.customId === 'ticket:cancel') {
      await interaction.reply({
        content: 'Demande annulée.',
        flags: MessageFlags.Ephemeral
      });
    }
  }

  if (interaction.isStringSelectMenu()) {
    if (interaction.customId !== 'role:select') return;

    await interaction.deferReply({
      flags: MessageFlags.Ephemeral
    });

    const selectedRole = interaction.values[0];

    // Vérifier les permissions et attribuer le rôle ici.

    await interaction.editReply(
      `Le choix "${selectedRole}" a été enregistré.`
    );
  }
});

Utiliser les menus déroulants

StringSelectMenuBuilder permet de proposer une liste d’options textuelles. Lorsqu’un utilisateur choisit une option, sa valeur est disponible dans interaction.values.

Un menu déroulant convient particulièrement aux situations suivantes :

  • Sélectionner un ou plusieurs rôles.
  • Choisir une langue.
  • Filtrer une catégorie de tickets.
  • Configurer les notifications.
  • Sélectionner une action dans une liste importante.

Discord propose aussi d’autres types de menus pour sélectionner des utilisateurs, des rôles, des salons ou des entités mentionnables. Un menu occupe normalement sa propre ligne d’action et ne se mélange pas avec des boutons dans la même ActionRowBuilder.

Gérer les interactions sous trois secondes

Lorsqu’un utilisateur clique sur un bouton ou utilise un menu, Discord transmet une interaction au bot. Avec discord.js, elle est généralement reçue dans l’événement interactionCreate.

Le bot doit accuser réception de cette interaction en environ trois secondes. Sans réponse dans ce délai, l’utilisateur risque de voir le message « This interaction failed ».

Plusieurs méthodes sont disponibles :

  • interaction.reply envoie une nouvelle réponse.
  • interaction.update modifie le message contenant le composant.
  • interaction.deferReply indique que la réponse arrivera plus tard.
  • interaction.deferUpdate accuse réception avant une future modification du message.
  • interaction.editReply complète une réponse différée.

Une réponse éphémère est visible uniquement par l’utilisateur concerné. Elle convient aux erreurs, confirmations privées et informations sensibles. Dans les versions récentes de discord.js v14, elle peut être demandée avec flags: MessageFlags.Ephemeral.

Utilisez deferReply avant une requête lente, par exemple un appel à une base de données. Pour une pagination ou un changement d’état visuel, deferUpdate évite d’envoyer immédiatement un nouveau message.

Construire des cas d’usage concrets

Un menu de rôles associe chaque option à un rôle Discord. Le handler doit vérifier que le rôle existe, que le bot peut le gérer et que l’utilisateur est autorisé à le recevoir.

Une confirmation oui ou non peut utiliser deux boutons Success et Danger. Après le clic, désactivez ou supprimez les composants afin d’empêcher une seconde exécution. Ce schéma est au coeur d’un système de tickets, où un bouton ouvre puis ferme un salon privé.

Pour une pagination, utilisez des identifiants comme page:previous et page:next. Le bot conserve ou encode la page actuelle, reconstruit l’embed, puis appelle interaction.update pour remplacer le contenu sans créer de nouveaux messages.

Appliquer les bonnes pratiques

Structurez les customId avec un format prévisible, par exemple role:add:123 ou ticket:close:456. Le handler peut ainsi séparer les segments et diriger l’interaction vers le bon service.

Un customId ne doit pas dépasser 100 caractères. Évitez d’y stocker des données sensibles ou un objet JSON volumineux. Préférez un identifiant court associé à des données enregistrées côté serveur.

Vous pouvez gérer les composants avec un handler global interactionCreate ou avec des collectors limités à un message et à une durée. Les collectors sont pratiques pour un flux temporaire. Un handler global convient mieux aux composants persistants qui doivent encore fonctionner après un redémarrage.

Prévoyez toujours une expiration. Après quelques minutes, modifiez le message pour désactiver les boutons ou retirez ses composants. Vérifiez également l’identité, les rôles et les permissions de la personne qui clique. Un bouton visible ne constitue jamais une autorisation suffisante.

Héberger le bot pour répondre 24/7

Les boutons et menus non liés nécessitent un processus actif qui écoute les interactions Discord. Si le bot est arrêté, les composants peuvent rester visibles, mais les clics ne seront pas traités. Les boutons Link, eux, continuent simplement d’ouvrir leur URL.

TalCloud propose un hébergement Bot Discord 24/7 dès 1,99 € par mois, compatible Node.js et Python, avec redémarrage automatique, stockage NVMe et infrastructure en France. Cette disponibilité est particulièrement importante pour les menus de rôles, tickets et boutons de modération qui doivent répondre à tout moment.

Conclusion

Les embeds structurent l’information tandis que les boutons et menus transforment un message en interface interactive. Avec EmbedBuilder, ButtonBuilder, ActionRowBuilder et StringSelectMenuBuilder, discord.js v14 permet de construire des expériences claires sans multiplier les commandes textuelles.

La fiabilité dépend ensuite du handler : identifiants structurés, réponse sous trois secondes, validation des permissions, expiration des composants et hébergement continu. Ces précautions permettent de proposer des interactions rapides, sûres et réellement utiles aux membres du serveur.