---
title: Execute JavaScript code automation
slug: execute-javascript-code
description: How the Execute JavaScript code automation works in Personizely: the context variables your code receives, when it runs, error handling, CSP needs and examples.
image: https://archbee-image-uploads.s3.amazonaws.com/1y1Fr-DMGy_apx7FwibyA/ORDTWfNKMXiY3ZRv0QSgP_personizely-docs-og.png
docTags: 
createdAt: 2026-10-16T20:07:52.196Z
---

The Execute JavaScript code automation runs your own JavaScript in the visitor's browser when they interact with a widget element. Use it to push events to your analytics, set a cookie, call a third-party script, or control the widget itself. Your code receives a small context object with the widget's element, the visitor and helper functions.

**Available on:** all platforms, except Webflow sites connected through the Webflow integration (see below).

## How it works

1. You add **Execute JavaScript code** to an element and write your code in the **JavaScript code** editor.
2. When the element's event happens, the code runs immediately in the visitor's browser, without waiting for the event to reach Personizely.
3. The context variables below are available as local constants.

## Context variables

```typescript
// Available inside your code as local constants
const variationId: number        // ID of the widget variation the visitor sees
const el: HTMLElement            // the widget's root element on the page
const type: 'popup' | 'callout' | 'bar' | 'embedded'
const visitor: {
  getId(): string                            // Personizely visitor ID
  get(field: string): any                    // a default field, e.g. visitor.get('email')
  getCustomFieldValue(fieldId: number): any  // a custom field by its ID
}
function close(): void                       // closes the widget
function changeStep(stepId: number): void    // shows another step of the widget
const helpers: {
  readCookie(name: string): string | undefined
  createCookie(name: string, value: string | null, seconds?: number): void
}
```

| Variable               | Notes                                                                                                                                                                                                                                                         |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `variationId`          | Useful to tell variations apart in your own analytics during a [widget A/B test](https://docs.personizely.net/widget-experiments-and-ab-testing)                                                                                                              |
| `el`                   | Query inside the widget with `el.querySelector(...)`                                                                                                                                                                                                          |
| `type`                 | The widget's type                                                                                                                                                                                                                                             |
| `visitor`              | Default field IDs include `email`, `firstName`, `lastName`, `phone`, `country`, `city`, `utmSource` and the others listed in [Visitor fields](https://docs.personizely.net/visitor-fields-reference). Values just submitted in this form are already included |
| `close()`              | Closes the widget as the close button would; counts as a close for Frequency                                                                                                                                                                                  |
| `changeStep(stepId)`   | Takes the numeric step ID                                                                                                                                                                                                                                     |
| `helpers.createCookie` | Sets a cookie on your top-level domain, shared with subdomains. `seconds` defaults to your website's cookie lifetime (default 1 year). Pass `null` as the value to delete the cookie                                                                          |
| `helpers.readCookie`   | Returns the cookie's value, or `undefined`                                                                                                                                                                                                                    |

The public JavaScript API (`window.ply`) is also available, for example `ply.trackGoal(...)` or `ply.runCampaign(...)`. See the [JavaScript API](https://docs.personizely.net/js-api).

The same context is available to the widget variation's custom JS, which runs each time the widget is displayed. See [Widget display options](https://docs.personizely.net/widget-display-options).

## Configuration

| Option          | What it does    | Values / default |
| --------------- | --------------- | ---------------- |
| JavaScript code | The code to run | JavaScript       |

## Behavior details and limitations

- **Errors are silent.** If your code throws, the error is swallowed so the widget keeps working. Open the page with `?ply_debug=1` to see "There's an error in your JS code" in the console, or wrap your code in `try`/`catch` and log the error yourself.
- **Don't redeclare the context names.** `const el = ...` or `let visitor` at the top level of your code is a syntax error, and the whole snippet won't run. Use other names.
- **Synchronous start.** The code runs as a plain function body. You can use promises and `return`, but not top-level `await`; wrap async work in an `async` function and call it.
- **It runs every time.** Each click or submit runs the code again.
- **Forms.** On a Form, the code runs on a successful submit, at the same time as the submission is sent.
- **Content Security Policy.** If your site has a CSP, it must allow `'unsafe-eval'` for scripts for this automation to run. See [Content Security Policy](https://docs.personizely.net/csp).
- **Webflow.** When the Webflow integration is connected, custom JavaScript, including this automation, doesn't run. Install the script tag manually instead. See [Install on Webflow](https://docs.personizely.net/install-webflow-square).
- **Preview runs it.** Browser automations run in [preview](https://docs.personizely.net/preview-qa), so guard code that sends data to production systems.

## Examples

Push a signup to Google Tag Manager:

```javascript
window.dataLayer = window.dataLayer || []
window.dataLayer.push({
  event: 'newsletter_signup',
  widget_variation: variationId,
  email_domain: (visitor.get('email') || '').split('@')[1]
})
```

Remember a choice for 30 days and close after 2 seconds:

```javascript
helpers.createCookie('prefers_wholesale', '1', 60 * 60 * 24 * 30)
setTimeout(close, 2000)
```

Go to a step based on your own logic:

```javascript
const cartTotal = window.myStore?.cart?.total || 0
changeStep(cartTotal > 100 ? 4512 : 4513)
```

## Troubleshooting

- **Nothing happens** → the code threw an error (check with `?ply_debug=1`), a context name was redeclared, or your CSP blocks `'unsafe-eval'`.
- **`visitor.get('email')` is empty** → the visitor hasn't submitted an email yet, or the field is a custom field: use `getCustomFieldValue(id)`.

## Related

- [Automations overview](https://docs.personizely.net/automations)
- [JavaScript API](https://docs.personizely.net/js-api)
- [Change field value](https://docs.personizely.net/change-field-value)
- [Content Security Policy](https://docs.personizely.net/csp)
- [Widget display options](https://docs.personizely.net/widget-display-options)
