Setting variables
Pass values into a bot's Landbot Fields from your page, and update them at runtime.
Every Landbot bot has a set of Landbot Fields (variables) that flow blocks can read and write.
You can set them from the server — using a Set a Variable block inside the flow itself — or from the client, before or during the conversation. This page covers the client-side mechanisms; there are two:
customData— imperative, programmatic. Pass an object to the widget constructor or callsetCustomDatalater from JS. The right choice when the values come from your page's runtime state (user profile, page context, A/B variant).- Hidden fields — declarative, URL-driven. Capture query-string parameters into named fields without writing any JS. The right choice for UTM tags, campaign codes, and any pre-fill that comes from how a user arrived at the bot.
You can mix the two.
At construction
Pass customData in the config you hand to the widget constructor. Fields are available before the very first flow block runs.
var myLandbot = new Landbot.Livechat({
configUrl: '...',
customData: {
email: 'visitor@example.com',
plan: 'pro',
utm_source: 'newsletter',
},
});
For the full embed snippet that loads the SDK, see the Widgets overview.
Inside the bot, read these with the @{fieldname} syntax — for example a Send Text block can say Hi @{email}, welcome back.
After load (from the parent page)
Call setCustomData on the widget instance to update fields after the widget has mounted. Useful when you don't know the value at construction time (e.g. waiting for an auth event).
myLandbot.onLoad(function () {
fetchUserProfile().then(function (profile) {
myLandbot.setCustomData({
email: profile.email,
first_name: profile.firstName,
});
});
});
Subsequent updates overwrite the same fields — there's no merge semantics to think about beyond plain object assignment.
From inside the bot
Inside a Code block in the flow, this is the widget scope. Call this.setCustomData(...) to set a field from JavaScript that runs as part of the conversation.
var landbotScope = this;
this.onLoad(function () {
var randomNumber = Math.floor(Math.random() * 9999) + 1;
landbotScope.setCustomData({ randomnumber: randomNumber });
});
This pattern is common for derived values (a generated ID, a formatted number, a computed timestamp) that should be in scope for later blocks.
You can also format a value in place and write it back to the same field:
this.setCustomData({
value: (Math.round("@{value}" * 100) / 100).toFixed(2),
});
Note
thisis only bound to the widget at the top level of a Code block or Custom JS panel. Inside nested functions (callbacks, event handlers,setTimeout)thisis something else. Capture it first:
var _landbot = this; function setupVariables() { _landbot.setCustomData({ ... }); }
>
> The `var landbotScope = this` pattern used elsewhere in the docs is the same idea — pick whichever name reads best to you.
## From URL query params (hidden fields)
**Hidden fields** are the declarative counterpart to `customData`. You declare a list of field names in the builder; any matching URL query parameter on the bot's URL is captured into a Landbot Field of the same name. No JavaScript involved.
### Setup
In the builder, open **Bot Settings → Hidden Fields** and add the parameter names you want to capture — for example: `name`, `email`, `utm_source`, `utm_campaign`, `ref`.
### Reading hidden fields in the flow
Append values to the bot URL as standard query parameters:
https://chats.landbot.io/v3/H-1234567/index.html?name=Jane&utm_source=newsletter
Inside the bot, read them with the usual `@{fieldname}` syntax:
Hi @{name}, welcome from @{utm_source}!
If a value can contain special characters (spaces, ampersands, slashes), encode it with `encodeURIComponent` before appending it to the URL:
```javascript
var url = 'https://chats.landbot.io/v3/H-1234567/index.html?name='
+ encodeURIComponent('Jane Smith & Co');
Constraints
- Lowercase only. Parameter names are case-sensitive and must be lowercase.
?Name=Janeis ignored. - Strings only. All values are stored as strings. To pass numbers you can recognise as booleans, pass
0.0/1.0instead of0/1(the bot otherwise interprets them as numeric falsy values in some blocks). - Reserved names. Don't use
location,welcome, ormessage— they collide with internal bot variables. - Avoid WordPress reserved query keys (e.g.
p,page_id,post_type) if you ever proxy the bot behind a WordPress URL — they can cause 404s.
Bridging hidden fields from a host page
Warning Hidden fields read the bot URL's query string. When a widget is embedded as an iframe (Fullpage, Popup, Container, Livechat, ContainerPopup), the iframe's
srcdoesn't inherit the host page's?key=value. Without bridging, parameters on the parent page won't reach the bot.
The fix is to read the parent URL on the host page and forward the values into the constructor's customData:
function readQuery() {
var params = new URLSearchParams(window.location.search);
return {
name: params.get('name') || undefined,
utm_source: params.get('utm_source') || undefined,
utm_campaign: params.get('utm_campaign') || undefined,
};
}
var myLandbot = new Landbot.Livechat({
configUrl: '...',
customData: readQuery(),
});
This gives the embedded bot the same pre-fill behaviour as a direct-URL visit, while keeping the Hidden Fields declaration in the builder as the source of truth for which params to capture.
Field naming
Fields you create via customData must use lowercase Latin letters, numbers, and _. Names are case-sensitive. If the field doesn't already exist in the bot's Field list it's created on first write.
Note A field set from JavaScript only becomes available to the flow after the next user input. If you need a value to be set before the first block runs, pass it via
customDataat construction.
Resetting context between interactions
setCustomData updates values; it doesn't restart the flow. When you need the bot to begin again with completely fresh customData — e.g. the user clicks "Ask about a different product" and you want a clean slate — destroy the current instance and create a new one.
function openWithProduct(product) {
if (myLandbot && myLandbot.destroy) {
myLandbot.destroy();
}
myLandbot = new Landbot.Popup({
configUrl: '...',
customData: {
product_id: product.id,
product_name: product.name,
product_price: product.price,
page_url: window.location.href,
},
});
myLandbot.open();
}
Decision rule:
- Use
setCustomDatawhen the conversation should continue with new or additional values. - Use
destroy+new Landbot.X({...})when the conversation should start over from the first block with a differentcustomDatapayload.
See also
Landbot.Widget.setCustomData— full method signature on the base class.- JavaScript execution — running JS inside the bot and from the parent page.
- Embedding in your app — moving data between widget and host page.