Custom Code Experiments
When to use custom code
Section titled “When to use custom code”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.
How custom code execution works
Section titled “How custom code execution works”When a visitor is bucketed into a variation, Optimizely injects the variation’s custom JavaScript and CSS into the page. The execution sequence is:
- The Optimizely snippet loads and evaluates targeting conditions
- The visitor is bucketed into a variation
- Variation CSS is injected into the page
<head>as a<style>block - Variation JavaScript executes in the global scope
- 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.
Add custom code to a variation
Section titled “Add custom code to a variation”- Open your experiment in the editor
- Select the variation you want to edit
- Click < / > (code icon) in the bottom toolbar to open the code editor
- Two tabs are available: JavaScript and CSS
- Write your code, then click Apply to preview
- Click Save when satisfied
Write variation JavaScript
Section titled “Write variation JavaScript”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.
// 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);
}); Use waitForElement for dynamic content
Section titled “Use waitForElement for dynamic content”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});Access experiment and variation data
Section titled “Access experiment and variation data”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);Write variation CSS
Section titled “Write variation CSS”CSS changes apply immediately when the variation activates. Use specific selectors to avoid conflicts with site styles.
/* 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.
Add shared project JavaScript
Section titled “Add shared project JavaScript”Code that should run across all experiments in a project — analytics wrappers, utility functions, polyfills — goes in the project-level JavaScript.
- Navigate to Settings > JavaScript in your project
- Add code that exports shared utilities on
window - This code executes before variation JavaScript
Handle single-page applications
Section titled “Handle single-page applications”In SPAs, the Optimizely snippet evaluates once on initial page load. For route changes handled by the client-side router:
- Use Optimizely’s URL targeting with regex or substring matching to activate on virtual page changes
- Alternatively, trigger experiment activation manually:
window.optimizely.push({ type: 'activate',});Call this after your router completes a navigation event.
Debugging custom code
Section titled “Debugging custom code”- Browser console — Check for JavaScript errors after the snippet loads
- Optimizely console log — Run
window.optimizely.get('log')to view internal event logs - Force variation — Append
?optimizely_x={experiment_id}:{variation_index}to the URL to force a specific variation - Network tab — Confirm the snippet is loading and returning the expected datafile
Common patterns
Section titled “Common patterns”| Pattern | Approach |
|---|---|
| Reorder DOM elements | Use insertBefore or appendChild inside waitForElement |
| Modify third-party widgets | Wait for the widget’s container, then manipulate its child elements |
| Add tracking to custom interactions | Call window.optimizely.push({ type: 'event', eventName: 'your_event' }) on user actions |
| Conditional variation logic | Read cookies, local storage, or data attributes before applying changes |
| Animate variation changes | Add CSS transitions or use requestAnimationFrame in JavaScript |
Troubleshooting
Section titled “Troubleshooting”| Issue | Cause | Fix |
|---|---|---|
| Code does not execute | JavaScript error before your code runs | Check the browser console for syntax errors |
| Element not found | DOM not ready when code runs | Use waitForElement instead of direct querySelector |
| Flicker on page load | Original content renders before variation applies | Add CSS to hide the container until modification completes, or use Performance Edge |
| Code works in preview but not live | Preview bypasses caching | Clear the CDN cache or wait for propagation |