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 call setCustomData later 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 this is only bound to the widget at the top level of a Code block or Custom JS panel. Inside nested functions (callbacks, event handlers, setTimeout) this is 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=Jane is ignored.
  • Strings only. All values are stored as strings. To pass numbers you can recognise as booleans, pass 0.0 / 1.0 instead of 0 / 1 (the bot otherwise interprets them as numeric falsy values in some blocks).
  • Reserved names. Don't use location, welcome, or message — 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 src doesn'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 customData at 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 setCustomData when 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 different customData payload.

See also