Every builder follows the same pattern: methods mutate internal state and return this. That makes a message read like a sentence.
await new Button(sock)
.setTitle('Order')
.setBody('Order #1042 is being prepared.')
.addReply('Track', 'track')
.addCopy('Copy code', 'MB-1042')
.send(jid);
Setters vs adders
| Kind | Examples | Behavior |
|---|---|---|
| Setter | setBody, setFooter, setImage |
Overwrites the previous value. |
| Adder | addReply, addButton, addText |
Appends an item to a list. |
| Builder | build, toCard, send |
Produces a payload or sends it. |
| Utility | getIds, hasId, peek |
Reads state without mutating it. |
Setters can be called repeatedly; the last value wins. Adders accumulate until you call clearButtons() or delete a node.
Call order
For most methods, order does not matter:
// Same result
button.setBody('Hello').addReply('OK', 'ok');
button.addReply('OK', 'ok').setBody('Hello');
Lists are the important exception:
button
.addSelection('Open menu') // makes the selection active
.makeSection('Food') // makes the section active
.makeRow('Fried rice', '$8', '', 'rice')
.makeRow('Chicken noodles', '$7', '', 'noodles');
makeSection() needs an active selection. makeRow() needs an active section. Otherwise the library throws an Error.
One builder, one message
Builders keep state. Reusing the same instance for two recipients carries the first message’s buttons into the second.
// Wrong
const button = new Button(sock).setBody('Hello').addReply('OK', 'ok');
await button.send(jidA);
await button.send(jidB); // still carries the same buttons
// Correct
const make = () => new Button(sock).setBody('Hello').addReply('OK', 'ok');
await make().send(jidA);
await make().send(jidB);
If you need to reuse an instance, call clearButtons() and explicitly reset the other fields.
Modifying an existing message
Every builder has loadFrom(msg), which reads an old payload and restores the builder state:
const rebuilt = new Button(sock).loadFrom(receivedMessage);
rebuilt.setBody('New body');
This is useful for replying to an interactive message or resending with small changes. AIRich also supports loadFrom() for sections and submessages.
Injecting raw fields
If WhatsApp has a field without a dedicated method, use addPayload():
new Button(sock)
.setBody('Hello')
.addReply('OK', 'ok')
.addPayload({
messageContextInfo: {
deviceListMetadata: {},
deviceListMetadataVersion: 2,
},
});
Fields are merged into the root payload. Be careful: a field with the same name can overwrite structure created by the library.
Chainable, not immutable
Methods return the same instance, not a copy. This matters when you keep a reference:
const base = new Button(sock).setBody('Hello');
const a = base.addReply('A', 'a'); // a === base
Create a new instance for variations, or carefully clone the raw state (methods and the socket cannot be cloned).