Widget Display Logic¶
This document provides a comprehensive overview of the decision flow that determines whether the Rose Widget displays on a given page. Understanding this flow is essential for debugging visibility issues and configuring the widget correctly.
For configuration options, see Client Configuration. For the technical implementation, see Widget Technical Knowledge.
Overview¶
The widget display decision involves multiple layers of checks, each with specific configuration options:
| Layer | Purpose | Configuration |
|---|---|---|
| Force Display | Bypass all checks (demo/testing) | Runtime forceDisplay param |
| Traffic Control | Gradual rollout percentage | site_configs.traffic_allocated |
| URL Whitelist | Only show on specific URLs | site_configs.display_only_on_urls |
| URL Blacklist | Never show on specific URLs | custom_config.exclude_url_patterns |
| Device Check | Mobile device handling | custom_config.enable_mobile |
| Copilot Mode | Show copilot form factor on CTA/form pages | global_client_config.features.copilot_mode |
Decision Flowchart¶
Note: When
copilot_modeis disabled (default), the widget uses default mode on all pages. Copilot mode activates on form tracking pages (CTA destinations, additional form tracking pages, thank-you pages) when the feature flag is enabled.
Layer 1: Force Display Override¶
Runtime parameter: forceDisplay
When forceDisplay is set to true, all other checks are bypassed and the widget always displays. This is used for:
- Demo pages
- Local development testing
- Client preview environments
InboundXLoader.init({
forceDisplay: true, // Bypasses all display restrictions
// ... other config
});
Layer 2: Traffic Control¶
Configuration: site_configs.traffic_allocated (0-100)
Controls what percentage of visitors see the widget using deterministic bucketing.
| Value | Behavior |
|---|---|
0 |
Widget never shows (domain disabled) |
1-99 |
Gradual rollout (e.g., 20 = 20% of visitors) |
100 |
Widget always shows (default) |
How Bucketing Works¶
- Generate a stable client ID (PostHog
distinct_idor localStorage UUID) - Hash the client ID to assign a bucket (0-99)
- If
bucket < traffic_allocated, show the widget
Key benefit: Same user always gets the same result across sessions. Increasing traffic from 20% to 80% keeps the original 20% enabled.
Code location: frontend/widget/src/lib/widgetInit/trafficControl.ts
Layer 3: URL Whitelist¶
Configuration: site_configs.display_only_on_urls (string array)
If defined, the widget only displays on URLs matching these patterns.
Matching Logic¶
- Uses simple substring matching (
includes()) - Trailing slashes are normalized before comparison
- If array is empty or undefined, no whitelist restriction applies
- Wildcards (
*) are NOT supported (unlikeexclude_url_patterns)
Code location: frontend/shared/src/context/SiteConfigContext.tsx (isCurrentURLAllowed, lines 665-694)
Layer 4: URL Blacklist¶
Configuration: custom_config.exclude_url_patterns (string array)
URLs matching these patterns will never show the widget.
{
"custom_config": {
"exclude_url_patterns": [
"www.example.com/blog*",
"www.example.com/admin*",
"www.example.com/login"
]
}
}
Matching Logic¶
- Uses full wildcard pattern matching via
matchesUrlPattern() - Supports
*wildcards (e.g.,*.example.com/internal*) - Checked after whitelist (if both defined, URL must pass both)
Code location: frontend/shared/src/utils/domain/siteRestrictions.ts
Layer 5: Device Check¶
Configuration: custom_config.enable_mobile (boolean)
| Value | Behavior |
|---|---|
false (default) |
Widget hidden on mobile devices |
true |
Widget visible on mobile devices |
Mobile Detection¶
Mobile detection uses User Agent string matching only:
Note: The 1236px breakpoint in constants is used for responsive layout adjustments within the widget, not for display decisions.
Code location: frontend/shared/src/utils/device/mobileDetection.ts
Layer 6: CTA Page Visibility¶
Configuration: custom_config.hide_search_bar_on_cta_pages (boolean)
This configuration marks pages where special behavior should apply. When copilot_mode is enabled, these pages show the copilot form factor instead of hiding the widget. When copilot_mode is disabled, the widget shows in default mode on all pages (the old "hide" behavior is replaced by the copilot feature).
What Counts as a "Form Tracking Page"?¶
"Form tracking page" is the umbrella term for any page where the widget tracks form submissions. This includes CTA destination pages. A page is considered a form tracking page if it matches any of these sources:
| Source | Configuration | Description |
|---|---|---|
| CTA Destinations | site_configs.ctas[].langs[].url |
URLs from CTA button definitions |
| Additional Destinations | custom_config.additional_cta_destinations |
Track-only URLs (no CTA buttons) ⚠️ |
| Form Tracking Pages | custom_config.additional_form_tracking_pages |
Extra patterns for form tracking |
| Thank-You Pages | custom_config.thank_you_pages |
Confirmation page patterns |
⚠️ Known issue:
additional_cta_destinationsis not currently checked byshouldEnableFormTracking()for widget hiding. It's used bygetAllCTADestinations()for other purposes but doesn't affect whether the widget is hidden on these pages.
Configuration Example¶
{
"custom_config": {
"hide_search_bar_on_cta_pages": true,
"additional_form_tracking_pages": [
"https://start.example.com/*",
"https://signup.example.com/trial"
],
"thank_you_pages": [
"*/thank-you*",
"*/confirmation*"
]
}
}
Code location: frontend/shared/src/config/SiteConfigManager.ts (shouldHideWidgetOnCTAPage, shouldEnableFormTracking)
Layer 7: Copilot Mode¶
Configuration: global_client_config.features.copilot_mode (boolean)
| Value | Behavior |
|---|---|
false (default) |
Widget always uses default mode (search bar, centered positioning) |
true |
Widget uses copilot mode on CTA/form tracking pages |
What is Copilot Mode?¶
Copilot mode is an alternative widget form factor designed to assist users in completing forms on CTA destination pages. Instead of hiding the widget on these pages, copilot mode provides:
- Minimized view: Small button in the bottom-right corner (instead of bottom-center search bar)
- Expanded view: Right-side panel (instead of centered chat popup)
- Welcome message: Initial assistant message with suggested questions related to the current page
- Form assistance: Helps users understand form fields and complete their tasks
When is Copilot Mode Used?¶
Copilot mode is activated when ALL of the following conditions are true:
copilot_modefeature flag is enabled inglobal_client_config.features- Current device is NOT mobile (copilot is never used on mobile devices)
- Current page is a form tracking page (CTA destination, additional form tracking page, or thank-you page)
Mobile Restriction¶
Copilot mode is never enabled on mobile devices. On mobile, the widget always uses the default form factor regardless of the copilot_mode feature flag setting. This ensures optimal user experience on smaller screens.
Configuration Example¶
To enable copilot mode:
-
Set the global feature flag in
global_client_configtable: -
Configure CTA pages in the site's
site_configs:
Optionally, add additional form tracking pages in custom_config:
Code location: frontend/shared/src/config/SiteConfigManager.ts (isCopilotModeEnabled, shouldUseCopilotMode)
Mode Transition: Copilot to Default¶
When a user navigates from a copilot mode page (CTA page) to a default mode page (non-CTA page), the widget handles the transition gracefully:
| Scenario | Behavior |
|---|---|
| User interacted with copilot (sent a message) | Conversation persists, widget shows in default mode with history |
| User only saw welcome message (no interaction) | Conversation is cleared, widget shows in collapsed state |
Why clear the welcome message?
The copilot welcome message ("How can I help you?") is a synthetic message designed to provide context-specific assistance on form pages. If the user navigates away without interacting, displaying this message on a non-CTA page creates poor UX because:
- The follow-up questions are related to the form context they left
- The welcome message appears without the user having initiated a conversation
- It suggests an ongoing conversation that never actually started
Technical implementation:
When the widget initializes in default mode, it checks if the conversation contains only the copilot welcome message (identified by COPILOT_WELCOME_MARKER). If so, the conversation is cleared and the widget resets to its initial collapsed state.
Code location: frontend/shared/src/components/RoseWidget.tsx (useEffect hook for clearing copilot-only conversation)
Key Functions Reference¶
| Function | Location | Purpose |
|---|---|---|
canInitializeWidget() |
widget/src/lib/widgetInit/initializationCheck.ts |
Orchestrates all initialization checks |
checkTrafficAllocation() |
widget/src/lib/widgetInit/trafficControl.ts |
Deterministic traffic bucketing |
isCurrentURLAllowed() |
shared/src/context/SiteConfigContext.tsx |
Checks URL whitelist (display_only_on_urls) |
isUrlAllowed() |
shared/src/utils/domain/siteRestrictions.ts |
Checks URL against exclusion patterns |
isCurrentDeviceAllowed() |
shared/src/utils/domain/siteRestrictions.ts |
Checks mobile device rules |
shouldHideWidgetOnCTAPage() |
shared/src/config/SiteConfigManager.ts |
Checks if page is CTA with hide config enabled |
shouldEnableFormTracking() |
shared/src/config/SiteConfigManager.ts |
Determines if page needs form tracking |
isCurrentPageDemoURL() |
shared/src/config/SiteConfigManager.ts |
Checks if page is a CTA destination |
isCopilotModeEnabled() |
shared/src/config/SiteConfigManager.ts |
Checks global copilot_mode feature flag |
shouldUseCopilotMode() |
shared/src/config/SiteConfigManager.ts |
Determines if copilot form factor should be used |
Debugging Display Issues¶
1. Check Browser Console¶
Enable debug logging to see the decision flow. The loader's debug option only enables loader logs. To see widget display-decision logs, enable debug mode after the widget loads:
// Option 1: Enable after widget is ready
window.InboundXWidget.onReady(() => {
window.InboundXWidget.enableDebug();
});
// Option 2: Check status and enable manually
window.InboundXWidget.getStatus(); // See current state
await window.InboundXWidget.enableDebug(); // Enable debug logs
Look for these log patterns:
- 🎯 shouldDisplayOnCurrentUrl - URL pattern evaluation
- 🚦 Traffic control - Traffic allocation check
- 📱 Mobile device detected - Device type detection
2. Verify Configuration¶
Check the site configuration in Supabase:
SELECT
domain,
traffic_allocated,
display_only_on_urls,
custom_config->'exclude_url_patterns' as exclude_patterns,
custom_config->'enable_mobile' as enable_mobile,
custom_config->'hide_search_bar_on_cta_pages' as hide_on_cta
FROM site_configs
WHERE domain = 'example.com';
3. Common Issues¶
| Symptom | Likely Cause | Check |
|---|---|---|
| Widget never shows | traffic_allocated = 0 |
Site config in Supabase |
| Widget missing on some pages | URL exclusion pattern | exclude_url_patterns config |
| Widget missing on mobile | Mobile not enabled | enable_mobile in custom_config |
| Widget missing on signup page | CTA page hiding | hide_search_bar_on_cta_pages |
| Widget shows inconsistently | Traffic bucketing | traffic_allocated < 100 |
4. Force Display for Testing¶
To bypass all restrictions temporarily:
Related Documentation¶
- Client Configuration - All configuration options
- Traffic Control - Detailed traffic allocation docs
- Widget Technical Knowledge - Implementation details
- Widget Development Setup - Local development guide