Scenario: a cafe bot that shows a categorized menu. The user picks a row, and the bot replies with product details.
1. Send the menu
import { Button } from './lib/MessageBuilder.js';
async function sendMenu(sock, jid) {
return new Button(sock)
.setTitle('Nusantara Cafe')
.setImage('https://example.com/menu-banner.jpg', {
caption: 'This season’s menu',
})
.setBody('Pick a category, then tap the item you want to order.')
.setFooter('Open daily, 8:00 AM – 10:00 PM')
.addSelection('Open menu')
.makeSection('Food')
.makeRow('Village-style fried rice', '$8', 'Spice level adjustable', 'fried-rice')
.makeRow('Chicken noodles with meatballs', '$7', 'Large portion', 'chicken-noodles')
.makeSection('Drinks')
.makeRow('Iced palm sugar latte', '$6', 'House blend', 'iced-latte')
.makeRow('Matcha latte', '$7', 'Oat milk', 'matcha')
.send(jid);
}
2. Handle the selection
sock.ev.on('messages.upsert', async ({ messages, type }) => {
if (type !== 'notify') return;
for (const msg of messages) {
if (msg.key.fromMe) continue;
const selectedId =
msg.message?.listResponseMessage?.singleSelectReply?.selectedRowId;
if (selectedId === 'fried-rice') {
await sendProductDetail(sock, msg.key.remoteJid, {
name: 'Village-style fried rice',
price: '$8',
description: 'Fried rice with egg, chicken, and vegetables. Spice level 0–5.',
id: 'fried-rice',
});
}
}
});
3. Product detail with a CTA
async function sendProductDetail(sock, jid, product) {
await new Button(sock)
.setImage(`https://example.com/${product.id}.jpg`)
.setBody(`${product.name}\n${product.price}\n\n${product.description}`)
.setFooter('Tap a button to add it to your order')
.addReply('Add to order', `add:${product.id}`)
.addUrl('View on web', `https://example.com/menu/${product.id}`, true)
.send(jid);
}
Notes
- WhatsApp limits how many rows render comfortably. Split large menus into sections or separate messages.
- The
headerargument ofmakeRow()is optional and is usually left empty. - Row IDs must be unique and stable. Do not use array indexes that may change.
- If you call
loadFrom(), the last selection and section are restored. Verify withconsole.dirbefore editing.
Variation: no image
Remove setImage(). The body becomes more compact, but keep readability on small screens in mind.
new Button(sock)
.setBody('Choose from the menu:')
.addSelection('Open menu')
.makeSection('Favorites')
.makeRow('Iced latte', '$6', '', 'iced-latte')
.send(jid);