Use markers to control activation
How to use markers to control activation of Optimizely Web Experimentation experiments in React applications with server-side rendering and hydration
You can use markers to control the activation of Optimizely Web Experimentation experiments in React applications with server-side rendering (SSR) and hydration. This gives you more fine-grained control over when and where Optimizely activates, which minimizes flicker and improves experiment reliability.
In SSR environments, Optimizely might apply experiment changes before React hydrates the page. This can lead to conflicts and visual inconsistencies. Markers signal to Optimizely when specific components are ready for experimentation.
Marker types
- CSS classes – Simple and easy to implement. Add a dedicated class like
optimizely-activate-my-experiment
to the target element. - Data attributes – More versatile and less likely to conflict with existing CSS. Use
data-optimizely-page="my-experiment"
or similar attributes. - HTML comments – Less common but useful for complex scenarios or when you want to avoid adding visible markers to the DOM.
Implementation strategies
Manual marker placement and activation
Manually add markers to your components and use Optimizely's manual activation mode.
Example React component
function ProductDetails() {
return (
<div className="product-details optimizely-activate-product-page">
{/* ... product details ... */}
</div>
);
}
Configuration
- Create a new Optimizely page with a manual activation mode.
- Set the targeting condition to when the element is present and use the CSS selector
.optimizely-activate-product-page
.
Activation code
useEffect(() => {
if (typeof window !== 'undefined' && window.optimizely) {
const observer = new MutationObserver((mutations) => {
if (document.querySelector('.optimizely-activate-product-page')) {
window.optimizely.push({ type: 'activate', pageId: 'YOUR_PRODUCT_PAGE_ID' });
observer.disconnect();
}
});
observer.observe(document.body, { childList: true, subtree: true });
}
},);
Automated marker injection
Use build tools (like Babel or Webpack) or custom scripts to inject markers automatically.
Example: Babel plug-in
// babel-plugin-optimizely-marker.js
module.exports = function({ types: t }) {
return {
visitor: {
JSXOpeningElement(path) {
if (t.isJSXIdentifier(path.node.name, { name: 'ProductDetails' })) {
path.node.attributes.push(
t.jsxAttribute(
t.jsxIdentifier('data-optimizely-page'),
t.stringLiteral('product-page')
)
);
}
}
}
};
};
Usage in .babelrc:
JSON
{
"plugins": [
"babel-plugin-optimizely-marker"
]
}
Component-level mapping
Define a mapping between components and Optimizely pages. Use a script or hook to inject markers based on this mapping.
Mapping configuration (optimizely-mapping.json):
{
"ProductDetails": "product-page",
"ShoppingCart": "cart-page"
}
Custom hook
function useOptimizelyMarker(componentName) {
useEffect(() => {
const pageName = optimizelyMapping;
if (pageName) {
// ... (logic to add marker and activate Optimizely page)
}
},);
}
Hybrid approach
Combine manual and automated strategies for flexibility. Use manual markers for complex scenarios and automated injection for simpler cases.
Best practices
- Use unique markers to avoid unintended activations.
- Monitor performance when using many markers or complex selectors.
- Test thoroughly in a staging environment.
- Store Optimizely page IDs securely.
Example with MutationObserver and useEffect
This example demonstrates how to use a MutationObserver
within a useEffect
hook to monitor the DOM for specific markers and activate corresponding Optimizely pages.
import { useEffect } from 'react';
function MyApp({ Component, pageProps }) {
useEffect(() => {
if (typeof window !== 'undefined' && window.optimizely) {
const pageIdMap = {
'optimizely-activate-product-page': 'YOUR_PRODUCT_PAGE_ID',
'optimizely-activate-cart-page': 'YOUR_CART_PAGE_ID',
// ... add more mappings as needed
};
const observer = new MutationObserver((mutations) => {
for (const pageMarker in pageIdMap) {
if (document.querySelector(`.${pageMarker}`)) {
window.optimizely.push({
type: 'activate',
pageId: pageIdMap,
});
console.log(`Optimizely page activated: ${pageIdMap}`);
}
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
}
},);
return <Component {...pageProps} />;
}
export default MyApp;
Updated 6 days ago