Skip to content

Write your own JavaScript and CSS

Add your own code to a test variant, or let AI write it, for changes that text and style edits can't make.

In this article

Most tests only need the visual editor: new texts, links, images and styles. Custom code is for everything else, for example a sticky add to cart bar, a new block with reviews, moving the price above the title, or opening a size guide on click.

Add code to a variant

  1. Open the test under Optimize → Experiments.
  2. Scroll to Custom code. Does the test have more than one variant? Pick the variant at the top of the card.
  3. Choose JavaScript or CSS and type or paste your code. The editor shows line numbers and colors, and marks lines with a problem as you type. Below the editor, Blocked lines must be fixed before you can save; Check lines are worth a look but don't block.
  4. Choose when the JavaScript runs under Run the JavaScript (see below), and what happens on an error under If the code fails.
  5. Click Save and check. Varly checks the code on every device and makes new screenshots of the original and the variant. This takes one to three minutes.

You can also open the code from the visual editor on your live page: click Code in the toolbar. There you can click Try on this page to see the result in your own browser right away. Nothing is saved until you click Save, and nothing is measured.

A variant can have only code, only edits from the visual editor, or both. Edits from the visual editor run first, then your code.

Let AI write the code

  1. Under Describe the change, write what you want in plain words, for example: "Add a bar at the bottom on mobile with the price and the add to cart button, shown after the visitor scrolls past the button."
  2. Click Write the code with AI. The AI looks at the elements on your page and writes JavaScript, CSS or both into the editor.
  3. Read the code. Change anything you like.
  4. Click Save and check.

The AI uses the same rules as you: Varly checks its code the same way, and tells you in plain words if it blocked something. Nothing is saved or launched until you do it. The change history notes that the code was first written by AI.

When the code runs

Choice When Good for
Right away As soon as Varly loads, often before the page is fully there. Code that only adds CSS classes or waits for elements itself.
When the page is ready When the HTML of the page has loaded. This is the default. Most changes.
When an element appears When an element matching the selector you enter is on the page, also if it comes later. Elements that load late, such as reviews or a cart drawer.

CSS is always added right away, before the page shows, so visitors don't see the original flash by.

Helpers you can use

Your code runs inside a function that gets a varly object:

  • varly.waitFor(selector, callback): calls callback with the element as soon as it is on the page, also if it appears later. Use it instead of document.querySelector, so your code never runs too early.
  • varly.track('event_name'): sends a custom event. You can pick it as the goal of the test under What counts as a conversion → Custom event. See Send your own events with Varly.track.
  • varly.onCleanup(() => { … }): runs when the variant stops, for example when a visitor goes to another page in a single-page shop. Remove what you added here.
  • varly.variant: the test and variant the visitor sees, with experimentId, experimentName, key and name.
  • varly.preview: true in the preview link and in Varly's check, so you can skip things that should only happen for real visitors.

setTimeout, setInterval and requestAnimationFrame work as usual. Varly catches errors in them and stops them when the variant stops.

Examples

A line of text above the product title:

varly.waitFor('h1', (title) => {
  if (document.querySelector('.varly-usp')) return
  const line = document.createElement('p')
  line.className = 'varly-usp'
  line.textContent = 'Free shipping and free returns'
  title.before(line)
  varly.onCleanup(() => line.remove())
})
.varly-usp { margin: 0 0 8px; font-size: 14px; color: #0f7a3d; }

Move the reviews block above the description, and count clicks on it:

varly.waitFor('#reviews', (reviews) => {
  const description = document.querySelector('#description')
  if (!description) return
  const before = reviews.previousElementSibling
  description.before(reviews)
  reviews.addEventListener('click', () => varly.track('reviews_clicked'))
  varly.onCleanup(() => before?.after(reviews))
})

A sticky add to cart button on mobile:

varly.waitFor('#add-to-cart', (button) => {
  if (window.innerWidth > 767 || document.querySelector('.varly-sticky')) return
  const bar = document.createElement('div')
  bar.className = 'varly-sticky'
  const copy = document.createElement('button')
  copy.type = 'button'
  copy.textContent = button.textContent
  copy.addEventListener('click', () => {
    varly.track('sticky_add_to_cart')
    button.click()
  })
  bar.append(copy)
  document.body.append(bar)
  varly.onCleanup(() => bar.remove())
})
.varly-sticky { position: fixed; left: 0; right: 0; bottom: 0; z-index: 50; padding: 12px; background: #fff; box-shadow: 0 -4px 16px rgb(0 0 0 / 0.1); }
.varly-sticky button { width: 100%; padding: 14px; border: 0; border-radius: 999px; background: #17161a; color: #fff; font: inherit; }

What Varly blocks

Varly reads every line before saving and refuses code that could harm your shop or your visitors. You get a short explanation with the line number.

  • Running text as code: eval, new Function, and setTimeout with text instead of a function.
  • Writing cookies or using cookieStore. Cookies belong to your site and your cookie banner.
  • Changing your site's own data in localStorage or sessionStorage. Your code may read anything, but only write keys that start with varly_test_, for example varly_test_bar_closed.
  • Sending data to other websites: fetch to another address, XMLHttpRequest, WebSocket, sendBeacon. fetch to a fixed path on your own site, such as fetch('/cart.js'), is fine.
  • Loading other scripts: import(), or adding script, iframe or object elements.
  • document.write, which can wipe the page.
  • Anything on checkout, payment or cookie elements, password or card fields, and sending forms or changing where a form sends its data.
  • In CSS: @import, http:// addresses, and anything that tries to run code.

Some things are allowed but marked Check, such as sending visitors to another page, alert() and reading cookies.

What happens when the code fails

The code runs inside a safety net, so an error never breaks your page. Under If the code fails you choose what happens next:

  • Stop the code and keep going (default): the code stops, other changes and the CSS stay.
  • Show the original: the visitor sees your normal page for the rest of the visit. They still count in the variant, so the results stay fair.

Either way, Varly logs the error with the test, so you can find it under Research → Analyses as a JavaScript error.

Before you can launch

A test with custom code has two extra checks on the launch checklist:

  1. A person approved the code. Code you save in Varly counts as approved. Code that an AI assistant saved through a chat (such as Claude or ChatGPT) is a draft: someone with the Owner or Editor role must read it and click I read this code, approve it.
  2. The check passed. After every change to the code, click Save and check (or Run the check). Varly opens your page on desktop, tablet and mobile, runs the code exactly like for visitors and looks for errors, for the element the code waits for, and for security settings that block it. The Custom code card shows the result, and the screenshots show the real result.

The test card and the experiments list show Uses custom code. Under Change history you see who changed the code, when and where.

Single-page shops

Some shops change pages without reloading, for example many headless and Shopify Hydrogen shops. Varly notices each page change. Is the new page still part of the test? Then Varly runs varly.onCleanup, then your code again. On a page outside the test, or on a checkout page, Varly stops the code and removes its CSS.

Sites with strict security settings

Some sites use a Content Security Policy (CSP) that limits which scripts and styles may run. Your code comes with the Varly script itself, so it doesn't need 'unsafe-inline' or 'unsafe-eval' for scripts. Your site does need to:

  • allow the Varly script in script-src (needed for Varly anyway), and
  • allow style tags in style-src with 'unsafe-inline', without a nonce or hash. Without it, your CSS and Varly's protection against flicker can't run.

Varly's check tells you when your site's security settings will block the test code. When visitors' browsers block it, the Custom code card says "Your site's security settings blocked the test code". Ask your developer to change the policy, or test without custom CSS.

Common problems

  • "Pause the test before you change its code." A running test can't change. Pause it, change the code, run the check and resume.
  • "Custom code is available for A/B and A/B/n tests." Multivariate and split URL tests don't have custom code. Use the visual editor, or a separate A/B test.
  • The check says the element was not found. The selector under When an element appears, or in varly.waitFor, doesn't match anything on the test page. Check it in your browser's developer tools.
  • My code works in the preview but not for some visitors. Check the device and browser. Very old browsers can't read modern JavaScript; Varly accepts code up to the 2020 version of JavaScript.
  • The code runs twice. In a single-page shop your code runs again after each page change. Check whether your element already exists, and remove it in varly.onCleanup.
Was this article helpful?

Still stuck? Email support@varly.ai and we'll help you out.

Related articles