Logo
  • Partner program
  • API
  • Support
Get Relevant Bits

Search

Getting started

Context app embed

Rule types

Analytics

Integrations

Technical

A/B testing

Theme developer guide

Relevant Bits API

Relevant Bits app

Plans & billing

Getting started
Getting started
/Theme developer guide
Theme developer guide
Theme developer guide

Theme developer guide

Theme developer guide

This guide covers how Context integrates with your Shopify theme, the loading strategies available, the events it fires, and best practices for working with dynamic components and inline scripts.

How Context Works

Context personalizes storefront content by replacing Shopify sections with targeted variations at page load. The process works in three phases:

  1. Initialization -- A lightweight script in the document <head> captures page context (template, customer, cart, product data) and prepares the page for personalization.
  2. Evaluation -- Context determines which recipe matches the current visitor based on your configured targeting rules.
  3. Rendering -- Matched variations are fetched and swapped into the page, replacing the default sections. Shopify section lifecycle events are fired so that theme scripts can re-initialize.

During phases 1 and 2, page sections are hidden to prevent a flash of default content. Once rendering completes, sections are revealed progressively and a ready event is dispatched.

Loading Strategies

Context needs to coordinate with your theme's JavaScript. Since many theme scripts initialize on the DOMContentLoaded event, Context provides three strategies for managing this interaction. You can configure the strategy in the Context app block settings under Loading strategy.

Redispatch (Default)

Setting value: "Redispatch (default)"

Context allows the browser's native DOMContentLoaded to fire as normal. Once personalization is complete, Context dispatches a new DOMContentLoaded event so that theme scripts re-initialize against the updated DOM.

When to use: This is the best starting point for most themes. It works well when your theme scripts bind to DOMContentLoaded and can tolerate the event firing a second time.

Trade-off: Scripts that run side effects on DOMContentLoaded (such as appending elements or starting animations) may execute twice -- once on the original event and once on the redispatched event.

Intercept and Queue

Setting value: "Intercept and queue"

An inline script runs very early in the page load and intercepts all DOMContentLoaded listeners registered on document and window. These listeners are queued and held until Context finishes rendering. At that point, the queued callbacks are executed in order against the personalized DOM.

While intercepting, document.readyState returns 'loading' so that scripts checking readiness behave correctly.

When to use: Use this strategy when you need to guarantee that theme scripts only see the personalized DOM. This prevents double-initialization and is the safest option for avoiding layout shift caused by scripts running before personalization.

Trade-off: Scripts that bypass addEventListener (for example, using inline onload attributes or polling document.readyState directly) will not be intercepted. Some third-party scripts may not be compatible with this approach.

Disabled

Setting value: "Disabled"

Context takes no action regarding DOMContentLoaded. The browser fires it natively and Context does not redispatch or intercept.

When to use: Use this when your theme handles initialization independently of DOMContentLoaded, or when you have custom integration logic that listens for the relevantbits:context:ready event directly.

Additional Loading Options

These settings are available in the Context app block and affect how content is loaded and displayed.

Show content before Context is loaded

Setting: "Show content before Context is loaded" (default: off)

By default, Context hides page sections while personalization is in progress to prevent a flash of default content. Enable this option if your personalized content is below the fold or if you prefer to show default content immediately and swap it when ready.

When this setting is off, sections receive a visibility: hidden style via the rb-context-loading CSS class on the <html> element. This class is removed once Context finishes.

Enable real-time updates

Setting: "Enable real-time updates" (default: off)

When enabled, Context monitors for cart changes and time-based triggers after the initial page load. If a visitor's cart changes or a time boundary is crossed, Context re-evaluates the targeting rules and re-renders sections if a different variation now matches.

Cart changes are detected automatically by monitoring Shopify's /cart API calls and the theme:cart:change event. Time boundaries are checked on a regular interval.

Rule caching

Setting: "Rule caching" (range: 1--24 hours, default: 24)

Controls how long a visitor's targeting results are cached in the browser before Context re-evaluates them on the next page load. Lower values mean more frequent re-evaluation but slightly more network overhead.

Combine matching recipes

Setting: "Combine matching recipes" (default: on)

When enabled, all matching recipes apply to a visitor in priority order. Each section can only display one variation -- the first matching recipe that targets that section wins. When disabled, only the single highest-priority matching recipe applies.

Events

Context fires custom DOM events at key points in its lifecycle. Use these to coordinate your theme scripts with personalization.

relevantbits:context:loaded

Fired on window when the Context script has finished loading and the window.relevantbits API is available. At this point, personalization has not started yet.

window.addEventListener('relevantbits:context:loaded', function () {
  // The Context API is now available
  // Personalization has not yet been applied
});

Typical use: Register configuration overrides or custom logic before Context begins rendering.

relevantbits:context:ready

Fired on window when Context has finished rendering all personalized sections and the page is fully ready. This is the primary event for theme developers to hook into.

window.addEventListener('relevantbits:context:ready', function () {
  // All personalized sections are now in the DOM
  // Safe to query personalized elements, start animations, etc.
});

Typical use: Initialize sliders, carousels, lazy-loaded images, or any JavaScript that depends on the final personalized DOM. This event fires regardless of which loading strategy is active.

shopify:section:load

Fired on each newly rendered section element when Context replaces a section with a personalized variation. This is the standard Shopify section lifecycle event.

document.addEventListener('shopify:section:load', function (event) {
  var section = event.target;
  var sectionId = event.detail.sectionId;
  // Initialize scripts for this specific section
});

Typical use: Shopify themes already handle this event for the theme editor. Context fires it so that section-specific initialization code runs for personalized content too.

shopify:section:unload

Fired on a section element just before Context replaces it with a new variation. This is the standard Shopify section cleanup event.

document.addEventListener('shopify:section:unload', function (event) {
  var section = event.target;
  var sectionId = event.detail.sectionId;
  // Clean up event listeners, timers, etc.
});

Typical use: Destroy carousels, disconnect observers, or clean up any resources tied to the outgoing section.

DOMContentLoaded (redispatched)

When using the Redispatch loading strategy, Context fires a new DOMContentLoaded event on document after personalization completes. This allows theme scripts bound to this event to re-initialize.

Note: This is a synthetic event. The browser's original DOMContentLoaded will have already fired before this one.

CSS Classes

Context adds CSS classes to the <html> element and to personalized sections. You can use these for custom styling.

Class
Applied to
When
rb-context-loading
<html>, individual sections
While personalization is in progress. Removed per-section as each finishes, and from <html> once all sections begin rendering.
rb-context-ready
<html>
After personalization is complete and all events have fired.
rb-context-section
Personalized section elements
Added to sections that have been replaced with a Context variation.

Example: Styling personalized sections

/* Add a transition to personalized sections */
.rb-context-section {
  animation: fadeIn 0.3s ease-in;
}

/* Hide a specific element until Context is ready */
.rb-context-loading .my-hero-banner {
  opacity: 0;
}
.rb-context-ready .my-hero-banner {
  opacity: 1;
  transition: opacity 0.3s;
}

Data Attributes

Personalized sections include data attributes for debugging and custom scripting.

Attribute
Description
data-ctx-recipe-id
The ID of the recipe that matched for this section.
data-ctx-recipe-name
The name of the matching recipe.
data-ctx-variation-id
The ID of the variation that was rendered.
data-ctx-variation-name
The name of the variation that was rendered.

Example: Reading variation data

document.addEventListener('shopify:section:load', function (event) {
  var section = event.target;
  var variationName = section.getAttribute('data-ctx-variation-name');
  if (variationName) {
    console.log('Context rendered variation:', variationName);
  }
});

Analytics Events

Context automatically tracks engagement analytics when visitors interact with personalized content. These events are recorded for reporting in the Context dashboard.

Event
Trigger
Description
session
Page load with a matching recipe
Recorded once per session (approximately every 24 hours per visitor).
view
Personalized section enters the viewport
Recorded when a visitor scrolls a personalized section into view. Uses IntersectionObserver.
click
Click inside a personalized section
Recorded on the first click within a personalized section.
checkout
Order completed
Recorded via the web pixel on the post-purchase page, with recipe attribution.

Third-party analytics forwarding

If the visitor has consented to tracking (via Shopify's Consent API), Context also forwards events to:

  • Klaviyo -- Pushed as Context session, Context view, and Context click events to the Klaviyo tracking queue.
  • Google Analytics -- Sent as context_session, context_view, and context_click events via gtag() if available on the page.

These events include the recipe name, variation name, section ID, and page URL, allowing you to build segments and reports in your analytics tools.

Inline Scripts

Overview

By default, Context loads external scripts and stylesheets referenced in your variation sections but does not execute inline <script> blocks. This is the safest behavior for most themes.

If your variations contain sections that rely on inline scripts (for example, a section with an embedded widget bootstrap script), you can enable the Run inline scripts experimental setting.

How it works

When Run inline scripts is enabled:

  1. Context scans the variation HTML for <script> tags without a src attribute.
  2. Data scripts are skipped -- <script> tags with types like application/json, application/ld+json, text/template, text/html, or text/x-template are never executed.
  3. Executable inline scripts are injected into the page and run in document order.
  4. Any external scripts that the inline code dynamically creates (for example, a bootstrap snippet that appends a <script src="..."> to the body) are tracked by Context so they can be cleaned up if the section is later replaced.

When Run inline scripts is disabled (the default):

  • Inline <script> blocks inside variations are not executed.
  • External <script src="..."> and <link rel="stylesheet"> tags are still loaded normally.
  • Scripts that were already on the page from the original section remain untouched.

External script and stylesheet handling

Regardless of the inline scripts setting, Context handles external resources from variation HTML as follows:

  • External scripts (<script src="...">) are appended to document.body once. Context tracks loaded script URLs to avoid duplicates across section replacements.
  • External stylesheets (<link rel="stylesheet">) are appended to document.head once. Duplicate detection prevents the same stylesheet from being loaded twice.
  • When a section is replaced with a new variation, previously loaded scripts and stylesheets from the old variation are cleaned up before the new ones are loaded.

Best Practices for Inline Scripts

1. Prefer external scripts over inline scripts.

Move initialization logic into external .js files referenced via <script src="...">. External scripts are loaded reliably regardless of the inline scripts setting and benefit from browser caching.

<!-- Preferred: External script -->
<script src="{{ 'my-section.js' | asset_url }}" defer></script>

<!-- Avoid: Inline script that may not execute -->
<script>
  initializeMySection();
</script>

2. Use shopify:section:load for section initialization.

Instead of relying on inline scripts, bind your initialization logic to the shopify:section:load event. This works with both the Shopify theme editor and Context replacements.

document.addEventListener('shopify:section:load', function (event) {
  var container = event.target;
  // Initialize components within this section
  var slider = container.querySelector('.my-slider');
  if (slider) {
    new SliderComponent(slider);
  }
});

3. Use relevantbits:context:ready for page-level initialization.

If you need to run logic after all personalization is complete, listen for the ready event rather than placing inline scripts in your sections.

window.addEventListener('relevantbits:context:ready', function () {
  // All sections are personalized, safe to initialize page-level features
  lazyLoadImages();
  initializeScrollAnimations();
});

4. Clean up resources on shopify:section:unload.

If your section scripts create timers, observers, or global event listeners, clean them up when the section is unloaded to prevent memory leaks and double-initialization.

document.addEventListener('shopify:section:unload', function (event) {
  var container = event.target;
  var slider = container.querySelector('.my-slider');
  if (slider && slider._sliderInstance) {
    slider._sliderInstance.destroy();
  }
});

5. Guard against double-initialization.

When using the Redispatch loading strategy, DOMContentLoaded fires twice. Protect your initialization code with a guard variable.

(function () {
  var initialized = false;
  document.addEventListener('DOMContentLoaded', function () {
    if (initialized) return;
    initialized = true;
    // One-time initialization here
  });
})();

6. Keep inline scripts idempotent when the setting is enabled.

If you must use inline scripts with the Run inline scripts setting enabled, ensure they are safe to run multiple times. Context may re-execute them if a section is replaced during real-time re-evaluation.

<script>
  // Guard against re-initialization
  if (!window.myWidgetLoaded) {
    window.myWidgetLoaded = true;
    loadWidget();
  }
</script>

Preview Mode

Context includes a preview mode for testing recipes before they go live. Append ?recipe={recipeId} to any storefront URL to preview a specific recipe.

In preview mode:

  • A banner appears at the top of the page showing which recipe is being previewed.
  • Analytics events (session, view, click) are not recorded.
  • Real-time re-evaluation is disabled.
  • If Combine matching recipes is enabled, you can preview multiple recipes by passing comma-separated IDs: ?recipe=id1,id2.

The window.relevantbits API

Context exposes a global window.relevantbits object with the following properties and methods available after the relevantbits:context:loaded event.

Property / Method
Description
window.relevantbits.enabled
Boolean indicating whether Context has been initialized on this page.
window.relevantbits.ready
Boolean indicating whether personalization is complete (only present in intercept mode).
window.relevantbits.template
The current Shopify template name (e.g., 'index', 'product', 'collection').
window.relevantbits.setOptions(options)
Override Context settings at runtime. Overrides persist in localStorage until cleared. See Runtime option overrides.
window.relevantbits.getContext()
Returns the stored context data including the matched recipe and visitor state.
window.relevantbits.setContext(data)
Updates the stored context data.

Runtime option overrides

You can override Context settings at runtime using setOptions. This is useful for testing different configurations without modifying the theme.

window.relevantbits.setOptions({
  debug: true, // Enable console logging
  enable_real_time: true, // Force real-time updates on
});

Overrides are stored in localStorage and persist across page loads. To clear them:

window.relevantbits.setOptions({});

Debug mode

Enable debug mode to see detailed logging in the browser console. This logs every step of the Context lifecycle including recipe matching, section rendering, script loading, and event recording.

window.relevantbits.setOptions({ debug: true });

Or enable it in the Context app block settings under Enable debug mode.

Integration Patterns

Pattern: Dynamic component initialization

If your theme has components (sliders, accordions, tabs) that need JavaScript initialization, use the section lifecycle events:

Pattern: Conditional logic based on personalization

window.addEventListener('relevantbits:context:ready', function () {
  // Check if a specific section was personalized
  var hero = document.querySelector(
    '.shopify-section[data-ctx-variation-name]',
  );
  if (hero) {
    console.log('Hero was personalized with:', hero.dataset.ctxVariationName);
  }
});

Pattern: Waiting for Context when it may not be installed

(function () {
  var timeout = setTimeout(function () {
    // Context did not load within 2 seconds, proceed with defaults
    initPage();
  }, 2000);

  window.addEventListener('relevantbits:context:ready', function () {
    clearTimeout(timeout);
    initPage();
  });

  function initPage() {
    // Your initialization logic
  }
})();

Troubleshooting

Sections flash default content before personalization

  • Ensure Show content before Context is loaded is disabled (the default).
  • Check that the Context app block is placed in your theme's <head> section.
  • If using a custom theme, verify that the rb-context-loading CSS class is not being overridden.

Theme scripts don't initialize after personalization

  • Try switching the loading strategy. Start with Redispatch, then try Intercept and queue if scripts run too early.
  • Ensure your scripts handle the shopify:section:load event for section-specific initialization.
  • Add a relevantbits:context:ready listener for page-level initialization.

Sliders, carousels, or tabs break after personalization

  • Verify that your component scripts listen for shopify:section:load and re-initialize when the event fires.
  • Add cleanup logic on shopify:section:unload to destroy existing instances before the section is replaced.

Third-party widgets don't appear in personalized sections

  • Enable Run inline scripts if the widget relies on an inline bootstrap script.
  • If the widget loads via an external script, ensure the <script src="..."> tag is included in your variation section's Liquid template.

Double-initialization of scripts

  • If using Redispatch, guard your DOMContentLoaded listeners against running twice (see best practices above).
  • Switch to Intercept and queue to prevent the original DOMContentLoaded listeners from firing before personalization.

On this page

  • Theme developer guide
  • How Context Works
  • Loading Strategies
  • Redispatch (Default)
  • Intercept and Queue
  • Disabled
  • Additional Loading Options
  • Show content before Context is loaded
  • Enable real-time updates
  • Rule caching
  • Combine matching recipes
  • Events
  • relevantbits:context:loaded
  • relevantbits:context:ready
  • shopify:section:load
  • shopify:section:unload
  • DOMContentLoaded (redispatched)
  • CSS Classes
  • Example: Styling personalized sections
  • Data Attributes
  • Example: Reading variation data
  • Analytics Events
  • Third-party analytics forwarding
  • Inline Scripts
  • Overview
  • How it works
  • External script and stylesheet handling
  • Best Practices for Inline Scripts
  • Preview Mode
  • The window.relevantbits API
  • Runtime option overrides
  • Debug mode
  • Integration Patterns
  • Pattern: Dynamic component initialization
  • Pattern: Conditional logic based on personalization
  • Pattern: Waiting for Context when it may not be installed
  • Troubleshooting
  • Sections flash default content before personalization
  • Theme scripts don't initialize after personalization
  • Sliders, carousels, or tabs break after personalization
  • Third-party widgets don't appear in personalized sections
  • Double-initialization of scripts
Logo

Version history

Link tree

Privacy

InstagramLinkedInYouTube
// In your theme's global JS file
(function () {
  function initSection(container) {
    // Initialize all dynamic components within this container
    container.querySelectorAll('[data-slider]').forEach(function (el) {
      new Slider(el);
    });
    container.querySelectorAll('[data-accordion]').forEach(function (el) {
      new Accordion(el);
    });
  }

  // Handle Shopify editor and Context section replacements
  document.addEventListener('shopify:section:load', function (event) {
    initSection(event.target);
  });

  // Handle initial page load after personalization
  window.addEventListener('relevantbits:context:ready', function () {
    document.querySelectorAll('.shopify-section').forEach(initSection);
  });
})();