# Web Experiment actions

Define how variants modify pages using actions tied to specific URLs.

Source: https://amplitude.com/docs/web-experiment/actions

---

On this page

- [Element changes](#element-changes)
- [URL redirect](#url-redirect)
- [Set up a URL redirect](#set-up-a-url-redirect)
- [Configuration limits](#configuration-limits)
- [SEO best practices for redirects](#seo-best-practices-for-redirects)
- [Custom code](#custom-code)
- [JavaScript](#javascript)
- [HTML](#html)
- [CSS](#css)
- [Examples](#examples)

# Web Experiment actions

Actions define how variants modify your site. Actions relate to variants rather than a specific page, and are applied to specific [Pages](/docs/web-experiment/pages) to control exactly where they apply.

Experiment applies variant actions during evaluation. This happens on the initial page load and any time state pushes to or pops from the session history. History state changes also cause the SDK to revert all applied element change and custom code actions before reevaluating and reapplying actions with the update page in mind.

## Element changes

Element changes modify existing elements on your site. Web Experiment applies these changes by editing the inner text of an element or appending style to the element based on the change you make in the visual editor.

The visual editor supports the following element changes:

- **[Display](/docs/web-experiment/set-up-a-web-experiment#display-and-visibility)**: Show or remove the element from the DOM.
- **[Visibility](/docs/web-experiment/set-up-a-web-experiment#display-and-visibility)**: Show or hide the element.
- **[Text](/docs/web-experiment/set-up-a-web-experiment#text)**: Update an element's inner text, color, and size.
- **[Background](/docs/web-experiment/set-up-a-web-experiment#background)**: Update a background image or color.
- **[Move](/docs/web-experiment/set-up-a-web-experiment#move)**: Move the position of an element.

## URL redirect

URL redirects load a new URL when a targeted user lands on a targeted page in your experiment. URL redirects happen on the client, and aren't the same as a server redirect with a `3xx` response. Use URL redirect when your variants are different URLs or pages—for example, when you're testing landing pages or different versions of a page built in a CMS. You can use URL redirect with standard A/B tests and [multi-armed bandits](/docs/feature-experiment/workflow/multi-armed-bandit-experiments).

URL redirects retain any query parameters on the original page URL. For example, you create a variant to redirect users from `https://example.com` to `https://example.com/get-started`. If a user clicks a link `https://example.com?utm_source=facebook`, Web Experiment redirects that user to `https://example.com/get-started?utm_source=facebook`.

### Set up a URL redirect

Set up URL redirect through the [Visual Editor](/docs/web-experiment/set-up-a-web-experiment#the-visual-editor) as a variant action. To add a URL redirect to a treatment variant:

1. [Create a Web Experiment](/docs/web-experiment/set-up-a-web-experiment) and open the Visual Editor.
2. Click the Treatment **three-dot** menu, select **Edit**, then under **Action** select **URL Redirect**.
3. In the URL Redirect panel, add each URL you want to test as a separate variant and click **Apply**.

### Configuration limits

Visual experimentation and Amplitude's low-code implementation apply the following limits when you use URL redirect:

- **Evaluation mode**: Limited to `local` to optimize test performance and minimize latency impact to end-users.
- **Bucketing unit**: Limited to `User` because evaluation mode is limited to *local*.
- **Keys**: Limited to `deviceID` because evaluation mode is limited to *local*.
- **Audience**: Limited to `all users` because URL redirect logic uses local evaluation mode.
- **Deployment**: Limited to the project API key to simplify setup requirements.

It's possible for the URL redirect test to have a [Sample Ratio Mismatch (SRM)](/docs/feature-experiment/troubleshooting/sample-ratio-mismatch). Redirect tests work by loading the redirected page, ideally as fast as possible. The sequence for this is:

**Control flow**

- Load control page HTML.
- Browser parses and loads dependencies (including the experiment script).
- Experiment script initializes, evaluates the user, and logs an impression if they're in control.

**Treatment flow**

- Experiment script evaluates the user → assigns them to treatment → triggers a redirect.
- Load treatment page HTML.
- Browser parses and loads dependencies (including the experiment script again).
- Experiment script initializes and logs the impression for treatment.

Because the treatment flow involves more steps before the impression is logged, users who bounce quickly (for example, after clicking an ad by mistake) are more likely to be counted in control than in treatment. This imbalance can show up as Sample Ratio Mismatch (SRM).

Researchers have observed similar effects: if treatment slows performance, more users may leave before logs are generated, leading to fewer recorded impressions. Conversely, faster performance can lead to more recorded users in treatment than in control.

To resolve SRM:

- **Reduce latency in evaluation**. The longer the delay before impressions are logged, the more pronounced the SRM effect.
- **Use local evaluation where possible**. For example, target only on browser properties instead of slower remote attributes like Country. This reduces the chance that users drop off before logging. Here’s a configuration example in Amplitude Experiment.

**Further reading:**

- [Causes of SRM](https://www.lukasvermeer.nl/srm/docs/causes/)
- [Pitfalls in Metric Interpretation (KDD 2017)](https://exp-platform.com/Documents/2017-08%20KDDMetricInterpretationPitfalls.pdf)
- [Diagnosing SRM in Online Experiments (KDD 2019)](https://exp-platform.com/Documents/2019_KDDFabijanGupchupFuptaOmhoverVermeerDmitriev.pdf)

### SEO best practices for redirects

Client-side redirects in experiments can affect how search engines index and rank your pages. Follow these best practices to minimize SEO impact.

#### Use temporary redirects during tests

Web Experiment uses client-side redirects that simulate a `302` temporary redirect through `window.location.replace`. This tells search engines to keep the original URL indexed during the experiment. Don't use `noindex` tags on your control page, as this removes it from search results.

After you pick a winner, implement a permanent server-side `301` or `308` redirect to consolidate link equity to the winning URL.

#### Add canonical tags on variant pages

Add a canonical tag on your experiment variant page that points back to the original (control) URL. This prevents search engines from indexing the variant as a separate page.

html

```
<!-- On the variant/redirect destination page -->
<link rel="canonical" href="https://example.com/original-page" />

#### Prefer server-side redirects for SEO-critical pages

For pages where SEO is critical, consider using server-side assignment and redirects at the edge (CDN) or server level. Server-side redirects execute before the page renders, which:

- Ensures search engine crawlers interpret redirects reliably.
- Minimizes page flicker for users.
- Provides proper HTTP status codes to crawlers.

If you must use client-side redirects, ensure the redirect logic runs in the `<head>` to reduce layout shift.

#### Keep tests short and avoid cloaking

Don't show different content or URLs to users compared to what Googlebot sees—search engines consider this cloaking and may penalize your site. End experiments promptly and remove test logic after concluding to avoid SEO drift.

#### Ensure goals cover both URLs

Define conversion goals that include both control and variant URLs. Web Experiment [preserves query parameters](#url-redirect) through redirects, so attribution and session continuity remain intact.

#### Use Search Console if issues arise

If your experiment variant page gets indexed unexpectedly, use [Google Search Console's URL removal tool](https://search.google.com/search-console/removals) as a temporary fix while you address the underlying issue.

## Custom code

Custom code is available on Growth and Enterprise plans only.

Web Experiment applies custom code actions as an optional part of the element changes action. With the custom code action, write custom JavaScript, CSS, and HTML for your site to add elements or customize your site in ways the visual editor doesn't support.

You apply custom code to specific [Pages](/docs/web-experiment/pages) using the **Apply to** dropdown, allowing you to run different code depending on which Page is active.

You can use custom code in tandem with the [element changes](#element-changes). For example, An engineer could build a custom code component with placeholder text. A non-technical user could then use the visual editor to edit the placeholder text without touching the custom code.

Web Experiment applies custom code to your site in the following order:

1. Adds **CSS** in a `<style>` tag in the page's `<head>`.
2. Parses **HTML** into a DOM element.
3. Wraps **JavaScript** in a function, and adds it to a `<script>` tag in the page's `<head>`.
4. Calls the wrapped **JavaScript** function and passes parsed HTML and utilities as arguments.

### JavaScript

Web Experiment wraps any custom JavaScript in a function, and calls it when the variant action applies. The function has two parameters you can use with your custom JavaScript code.

- `html`: The custom HTML code parsed as a DOM element object.
- `utils`: An object that contains utility functions you can use in your custom code.

#### Utilities

Web Experiment provides the following utilities:

- `waitForElement(selector: string): Promise<Element>`: Returns a promise that resolves when it finds an element that matches the selector in the DOM. Uses `MutationObserver` to listen for elements.

- `remove: (()=> void) | undefined`: A function that you can set inside the JavaScript you inject. Web Experiment calls this function on page change, when Amplitude reevaluates experiments and reapplies variants.

  This function can be useful for cleaning up changes to the page in single page apps, where the page doesn't fully reload. For example, if you inject an HTML element on a specific page, set this function to remove that element when the page changes.

### HTML

Web Experiment parses custom HTML as a DOM element, and passes it to the custom JavaScript code to insert it. This HTML can use existing CSS styles and classes, or new CSS that you define.

### CSS

Custom CSS styles you can use to manipulate existing CSS classes and styles, or add new styles for elements you add with custom HTML. Web Experiment adds custom CSS to a `<style>` tag in the page's `<head>` element.

### Examples

Generative AI like ChatGPT or equivalents can create HTML and CSS for simple elements. The modal and banner examples below were both initially generated initially by ChatGPT, then modified.

#### Insert an element

To insert an element onto your page, follow this simple pattern.

1. Write the HTML and CSS for the element you want to add to the page.

2. Identify the selector of the part element you want to insert your new element into. This is often just the `body`.

3. Paste the following JavaScript code, and update `PARENT_SELECTOR` with the parent element selector from step 2.

   js

   utils.waitForElement("PARENT_SELECTOR").then(function (e) {
     e.appendChild(html);
     utils.remove = function () {
       html.remove();
     };
   });

If you want to insert your element into the parent element at a specific position, use `insertBefore()` instead of `appendChild()`.

#### Add a banner

This example adds a discount code banner to the top of the page.

utils.waitForElement("body").then(function (e) {
  e.insertBefore(html, e.firstChild);

#### Add a modal

This example adds a modal to the page after a 1 second delay.

var modal = html;
utils.waitForElement("body").then(function (body) {
  // Append the modal element to the body.
  body.appendChild(modal);

  // Get the close button element
  var closeBtn = document.getElementsByClassName("close")[0];

  // When the user clicks on the close button (x), close the modal
  closeBtn.onclick = function () {
    modal.style.display = "none";

  // When the user clicks anywhere outside of the modal, close it
  window.onclick = function (event) {
    if (event.target == modal) {
    }

  // Show the modal after a 1 second delay.
  window.setTimeout(function () {
    modal.style.display = "block";
  }, 1000);

  // Remove the modal on teardown.
    modal.remove();

Was this helpful?

<!--$-->

<!--/$-->
