Skip to content

Custom Code Experiments

⏱ 20 minutes intermediate

The visual editor handles most visual changes. Custom code is for everything else — dynamic content swaps, third-party widget modifications, conditional logic based on page state, animations, and changes to elements that the visual editor cannot select. If your variation requires reading data from the DOM, calling an API, or manipulating elements that render after page load, custom code is the right approach.

When a visitor is bucketed into a variation, Optimizely injects the variation’s custom JavaScript and CSS into the page. The execution sequence is:

  1. The Optimizely snippet loads and evaluates targeting conditions
  2. The visitor is bucketed into a variation
  3. Variation CSS is injected into the page <head> as a <style> block
  4. Variation JavaScript executes in the global scope
  5. Shared project JavaScript (if configured) executes after all variation code

Your code runs once per page load. For single-page applications, you may need to listen for route changes.

  1. Open your experiment in the editor
  2. Select the variation you want to edit
  3. Click < / > (code icon) in the bottom toolbar to open the code editor
  4. Two tabs are available: JavaScript and CSS
  5. Write your code, then click Apply to preview
  6. Click Save when satisfied

Your JavaScript runs in the global scope with access to the full DOM and the window.optimizely API. Use the Optimizely utility library ($) for common operations.

Replace a hero section dynamically
javascript
// Wait for the target element to exist in the DOM
var utils = window.optimizely.get('utils');

utils.waitForElement('.hero-container').then(function(heroEl) {
  // Replace hero headline
  var headline = heroEl.querySelector('h1');
  headline.textContent = 'Ship faster with confidence';

  // Swap hero image
  var img = heroEl.querySelector('img.hero-image');
  img.src = 'https://cdn.example.com/images/new-hero.jpg';
  img.alt = 'Team collaborating on product launch';

  // Add a new CTA button
  var cta = document.createElement('a');
  cta.href = '/free-trial';
  cta.className = 'btn btn-primary hero-cta';
  cta.textContent = 'Start free trial';
  heroEl.appendChild(cta);
});

Many sites render content asynchronously. Directly querying the DOM on script execution may return null. Always use waitForElement to wait for the target element.

var utils = window.optimizely.get('utils');
utils.waitForElement('#dynamic-pricing-table').then(function(el) {
// Safe to modify the element here
});

Read the current experiment context from the Optimizely API.

var state = window.optimizely.get('state');
var activeExperiments = state.getActiveExperimentIds();
var variationMap = state.getVariationMap();
console.log('Active experiments:', activeExperiments);
console.log('Variation assignments:', variationMap);

CSS changes apply immediately when the variation activates. Use specific selectors to avoid conflicts with site styles.

Override styles for a variation
css
/* Increase CTA button contrast */
.hero-container .btn-primary {
  background-color: #e63946;
  color: #ffffff;
  font-size: 18px;
  padding: 14px 32px;
  border-radius: 8px;
}

/* Hide secondary navigation for this variation */
.nav-secondary {
  display: none !important;
}

/* Adjust hero layout for single-column */
.hero-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

Tip: Use !important sparingly and only when site CSS specificity prevents your changes from applying.

Code that should run across all experiments in a project — analytics wrappers, utility functions, polyfills — goes in the project-level JavaScript.

  1. Navigate to Settings > JavaScript in your project
  2. Add code that exports shared utilities on window
  3. This code executes before variation JavaScript

In SPAs, the Optimizely snippet evaluates once on initial page load. For route changes handled by the client-side router:

  1. Use Optimizely’s URL targeting with regex or substring matching to activate on virtual page changes
  2. Alternatively, trigger experiment activation manually:
window.optimizely.push({
type: 'activate',
});

Call this after your router completes a navigation event.

  1. Browser console — Check for JavaScript errors after the snippet loads
  2. Optimizely console log — Run window.optimizely.get('log') to view internal event logs
  3. Force variation — Append ?optimizely_x={experiment_id}:{variation_index} to the URL to force a specific variation
  4. Network tab — Confirm the snippet is loading and returning the expected datafile
PatternApproach
Reorder DOM elementsUse insertBefore or appendChild inside waitForElement
Modify third-party widgetsWait for the widget’s container, then manipulate its child elements
Add tracking to custom interactionsCall window.optimizely.push({ type: 'event', eventName: 'your_event' }) on user actions
Conditional variation logicRead cookies, local storage, or data attributes before applying changes
Animate variation changesAdd CSS transitions or use requestAnimationFrame in JavaScript
IssueCauseFix
Code does not executeJavaScript error before your code runsCheck the browser console for syntax errors
Element not foundDOM not ready when code runsUse waitForElement instead of direct querySelector
Flicker on page loadOriginal content renders before variation appliesAdd CSS to hide the container until modification completes, or use Performance Edge
Code works in preview but not livePreview bypasses cachingClear the CDN cache or wait for propagation