Widget Configuration
Complete reference for all Nexus Chat Widget configuration options.
Required Configuration
window.NexusChatWidget.init({
experienceId: "your-experience-id", // Required: Your unique experience ID
apiUrl: "https://nexus-api.uat.knowbl.com/api/v2", // Required: Nexus API endpoint
});
Button Configuration
Customize the floating chat button (only applies in floating mode):
{
button: {
position: "bottom-right", // 'bottom-right', 'bottom-left', 'top-right', 'top-left'
text: "Chat with us", // Button text
backgroundColor: "#007bff", // Background color
textColor: "#ffffff", // Text color
hoverBackgroundColor: "#0056b3", // Hover state color
borderRadius: "50px", // Border radius (use '50%' for circular)
size: "medium", // 'small', 'medium', 'large'
iconUrl: "/path/to/icon.svg", // Custom icon URL
},
}Window Configuration
Configure the chat window appearance:
{
window: {
width: "384px", // Window width
height: "600px", // Window height
backgroundColor: "#ffffff", // Background color
borderRadius: "10px", // Border radius
boxShadow: "0 4px 12px rgba(0,0,0,0.15)", // Shadow
// Top accent bar
topAccentColor: "#005a9c", // Blue accent bar at top
topAccentHeight: "4px", // Height of accent bar
// Sizing modes (allows users to toggle between sizes)
sizing: {
showToggle: true, // Show size toggle button
rememberSize: true, // Remember user's size preference
defaultMode: "normal", // Default size mode
modes: [
{ name: "normal", width: "384px", height: "600px" },
{ name: "expanded", width: "600px", height: "800px" },
],
},
// Header configuration
header: {
show: true, // Show header
title: "Support Chat", // Header title
showCloseButton: true, // Show close button
showSizeToggle: true, // Show size toggle
backgroundColor: "#007bff", // Header background
textColor: "#ffffff", // Header text color
iconColor: "#ffffff", // Icon color
padding: "1rem", // Header padding
},
},
}Message Bubbles
Customize message appearance:
{
bubbles: {
// Global default max-width for all message bubbles (default: "85%")
// Applies consistently to all message types. In flex layout, percentages are
// relative to the container width, so messages naturally align at the same width.
// Role-specific maxWidth settings will override this default
defaultMaxWidth: "85%", // Can use percentage ("85%") or fixed width ("600px")
// User messages
user: {
alignment: "right", // 'left' or 'right'
backgroundColor: "#ffffff", // Background color
backgroundColorHover: "#f5f5f5", // Optional: specific hover color (disables brightness filter)
enableHoverEffect: true, // Optional: enable/disable hover effect (default: true)
textColor: "#000000", // Text color
borderRadius: "18px", // Border radius (supports asymmetric like "10px 10px 0px 10px")
border: "1px solid #e0e0e0", // Full border shorthand
// OR use individual properties:
// borderWidth: "1px",
// borderColor: "#e0e0e0",
// borderStyle: "solid",
padding: "0.5rem 1rem", // Internal spacing (CSS shorthand, default: "0.5rem 1rem")
},
// Assistant messages
assistant: {
backgroundColor: "#f0f0f0", // Background color (also used for avatar wrapper)
enableHoverEffect: true, // Optional: enable/disable hover effect (default: true)
textColor: "#000000", // Text color
borderRadius: "18px", // Border radius
showAvatar: true, // Show avatar
avatarUrl: "/path/to/avatar.png", // Custom avatar URL (images wrapped in circular background)
// Note: Avatar images are centered in a circular container with the backgroundColor.
// For SVG icons or small images, padding is added automatically for better appearance.
// Avatars themselves do NOT have hover effects, only message bubbles do.
// No border for assistant messages in this example
padding: "0.5rem 1rem", // Internal spacing (CSS shorthand, default: "0.5rem 1rem")
},
// Agent messages (for live agent handover)
agent: {
backgroundColor: "#e8f5e9", // Background color (also used for avatar wrapper)
textColor: "#000000", // Text color
borderRadius: "18px", // Border radius
border: "2px solid #4caf50", // Distinct border for agent
showAvatar: true, // Show avatar
avatarUrl: "/path/to/agent-avatar.png", // Optional custom avatar URL
showAgentBadge: true, // Show "Live Agent" badge
badgeText: "Live Agent", // Custom badge text
badgeColor: "#4caf50", // Badge background color
padding: "0.5rem 1rem", // Internal spacing (CSS shorthand, default: "0.5rem 1rem")
},
spacing: "0.75rem", // Space between messages
animation: "fade", // 'fade', 'slide', or 'none'
showTimestamps: false, // Show message timestamps
},
}Message Bubble Padding
Configure the internal spacing (padding) of message bubbles for each role:
The padding property accepts CSS shorthand values:
"8px 16px"- vertical horizontal (default)"8px 16px 8px 16px"- top right bottom left"16px"- all sides equal
Each role (user, assistant, agent) can have independent padding configuration. If not specified, the default padding of "8px 16px" (8px top/bottom, 16px left/right) is applied.
Example:
bubbles: {
user: {
padding: "8px 16px", // Larger padding for user messages
backgroundColor: "#2563eb",
textColor: "#ffffff",
},
assistant: {
padding: "8px 16px", // Standard padding
backgroundColor: "#f3f4f6",
textColor: "#111827",
},
agent: {
padding: "10px 20px", // Medium padding for live agent
backgroundColor: "#16a34a",
textColor: "#ffffff",
},
}Avatar Styling
Customize the appearance of avatars displayed next to message bubbles and in event cards. Avatars can be configured globally and overridden per role or event type.
Configuration Locations
Avatar styling can be configured in multiple places with increasing specificity:
- Global default for all bubble avatars:
bubbles.defaultAvatar - Per-role bubble avatars:
bubbles.assistant.avatarandbubbles.agent.avatar - Global default for all event avatars:
events.default.avatar - Per-event type avatars:
events.agentHandover.avatar
Role-specific and event-specific configurations override global defaults.
Avatar Configuration Object
Each avatar configuration uses the avatar object with the following properties:
avatar: {
enabled: true, // Show/hide avatar
url: "https://...", // Avatar image URL
size: "md", // Size preset or custom CSS
borderRadius: "circle", // Shape preset or custom CSS
border: "2px solid #3b82f6", // Border shorthand
// OR use granular border properties:
borderWidth: "2px",
borderColor: "#3b82f6",
borderStyle: "solid",
margin: "0 12px 0 0" // Spacing around avatar
}Note: The older showAvatar and avatarUrl properties are deprecated but still supported for backward compatibility. New implementations should use avatar.enabled and avatar.url instead.
Available Properties
enabled: Whether to show the avatar (default:falsefor assistant,truefor agent and events)url: Avatar image URL. If not provided, falls back to default role-specific iconsize: Avatar dimensions (see Size Options below)borderRadius: Corner rounding (see Border Radius Options below)border: Complete border definition using CSS shorthand (e.g.,"2px solid #000")borderWidth,borderColor,borderStyle: Granular border properties (alternative tobordershorthand)margin: CSS margin shorthand for spacing around the avatar (e.g.,"0 12px 0 0")
Size Options
Preset Values:
| Preset | CSS Value | Pixels | Tailwind Equivalent |
|---|---|---|---|
"xs" | 20px | 20x20 | w-5 h-5 |
"sm" | 24px | 24x24 | w-6 h-6 |
"md" | 32px | 32x32 | w-8 h-8 (default) |
"lg" | 40px | 40x40 | w-10 h-10 |
"xl" | 48px | 48x48 | w-12 h-12 |
Custom CSS Values:
You can also use any valid CSS size value like "56px", "3rem", "10vw", etc.
Border Radius Options
Preset Values:
| Preset | CSS Value | Description |
|---|---|---|
"circle" | 9999px | Fully rounded (default) |
"rounded" | 8px | Slightly rounded |
"square" | 0 | No rounding |
Custom CSS Values:
Use any valid CSS border-radius value like "50%", "12px", "1rem", etc.
Basic Example
const config = {
bubbles: {
// Global default for all bubble avatars
defaultAvatar: {
size: "md",
borderRadius: "circle"
},
assistant: {
avatar: {
enabled: true,
size: "lg", // Override: larger for assistant
borderRadius: "circle",
border: "2px solid #3b82f6"
},
backgroundColor: "#f3f4f6",
textColor: "#111827"
},
agent: {
avatar: {
enabled: true,
url: "https://api.dicebear.com/7.x/avataaars/svg?seed=agent",
size: "lg",
borderRadius: "rounded",
borderWidth: "3px",
borderColor: "#10b981",
borderStyle: "solid",
margin: "0 16px 0 0"
},
backgroundColor: "#dcfce7",
textColor: "#166534"
}
}
};Event Avatar Configuration
Configure avatars for event cards (like agent handover):
const config = {
events: {
// Global default for all event avatars
default: {
avatar: {
size: "lg",
borderRadius: "circle"
}
},
agentHandover: {
enabled: true,
showCard: true,
avatar: {
enabled: true,
url: "https://...", // Optional: override agent avatar URL
size: "xl", // Larger avatar for handover card
borderRadius: "rounded",
border: "4px solid #f59e0b"
}
}
}
};Custom CSS Values Example
Use precise CSS values for complete control:
const config = {
bubbles: {
assistant: {
avatar: {
enabled: true,
size: "56px", // Custom size
borderRadius: "12px", // Custom radius
border: "2px dashed #8b5cf6",
margin: "0 20px 0 0"
}
},
agent: {
avatar: {
enabled: true,
size: "64px",
borderRadius: "50%", // Percentage-based
border: "4px double #ec4899",
margin: "0 24px 0 0"
}
}
}
};Migration from Deprecated Properties
If you’re currently using the deprecated showAvatar and avatarUrl properties, here’s how to migrate:
Before (deprecated, but still supported):
bubbles: {
assistant: {
showAvatar: true,
avatarUrl: "https://example.com/avatar.png"
}
}After (recommended):
bubbles: {
assistant: {
avatar: {
enabled: true,
url: "https://example.com/avatar.png"
}
}
}Important Notes:
- The deprecated properties continue to work as fallbacks for backward compatibility
- TypeScript users will see deprecation warnings when using
showAvatarandavatarUrl - New configurations should use the
avatarobject for better organization and access to advanced styling options - If both old and new properties are present, the new
avatarproperties take precedence
Complete Avatar Styling Example
const config = {
experienceId: "your-experience-id",
apiUrl: "https://nexus-api.uat.knowbl.com/api/v2",
bubbles: {
// Set global defaults
defaultAvatar: {
size: "md",
borderRadius: "circle",
margin: "0 12px 0 0",
},
assistant: {
avatar: {
enabled: true,
size: "lg",
borderRadius: "circle",
border: "3px solid #3b82f6",
margin: "0 12px 0 0",
},
backgroundColor: "#f3f4f6",
textColor: "#111827",
},
agent: {
avatar: {
enabled: true,
url: "https://api.dicebear.com/7.x/avataaars/svg?seed=agent",
size: "lg",
borderRadius: "circle",
borderWidth: "3px",
borderColor: "#10b981",
borderStyle: "solid",
margin: "0 12px 0 0",
},
backgroundColor: "#dcfce7",
textColor: "#166534",
},
},
events: {
default: {
avatar: {
size: "lg",
},
},
agentHandover: {
enabled: true,
showCard: true,
avatar: {
enabled: true,
size: "xl",
borderRadius: "rounded",
border: "4px solid #f59e0b",
},
},
},
};
For a live demonstration of various avatar styling configurations, see the Avatar Styling Demo.
Input Field
Configure the message input area:
{
input: {
placeholder: "Type your message...", // Placeholder text
placeholderColor: "#999999", // Custom placeholder color
placeholderFontStyle: "italic", // Italicize placeholder text
backgroundColor: "#ffffff", // Background color
textColor: "#000000", // Text color
borderColor: "#e0e0e0", // Border color
borderWidth: "1px", // Border width
borderRadius: "24px", // Border radius
padding: "0.75rem 1rem", // Textarea padding
gap: "0.5rem", // Gap between textarea and buttons
fontFamily: "'Roboto', sans-serif", // Custom font for input text
fontSize: "sm", // Font size (e.g., "sm", "md", "lg")
// Send button configuration (recommended)
sendButton: {
text: "Send", // Custom text (replaces icon)
style: "filled", // "filled" or "text"
color: "#007bff", // Base color
textColor: "#ffffff", // Text/icon color
backgroundColor: "#007bff", // Override background
padding: "0.75rem 1.25rem", // Button padding
borderRadius: "8px", // Button border radius
border: "none", // Button border
fontSize: "base", // "xs", "sm", "base", "lg", "xl", or CSS value
fontWeight: "semibold", // "light", "normal", "medium", "semibold", "bold"
disabledOpacity: "0.5", // Opacity when disabled
disabledTextColor: "#999999", // Text color when disabled
},
// OR: Text-style send button
// sendButton: {
// style: "text",
// color: "#007bff",
// backgroundColor: "transparent",
// border: "none",
// },
micButtonColor: "#007bff", // Microphone button color (not yet implemented)
maxRows: 4, // Max textarea rows
maxHeight: "120px", // Max height as CSS value - takes precedence over maxRows
minRows: 1, // Min textarea rows
integratedButton: false, // Button inside input border
},
}Input Height Configuration
The input field supports two methods for controlling textarea height:
CSS Height Values (Recommended for precise control):
- Use
maxHeightwith CSS values like"120px","10vh","128px" - Provides pixel-perfect control over textarea dimensions
- Supports all standard CSS units:
px(recommended),vh,vw,rem,em
Row-Based Sizing (Traditional approach):
- Use
maxRowsandminRowsfor automatic height based on line count - Fully backwards compatible with existing configurations
Height Precedence:
- If
maxHeightis set → uses CSS height value (most specific) - If only
maxRowsis set → calculates height from rows x line height - If neither is set → defaults to 2 rows
Important Notes:
maxHeighttakes precedence overmaxRowswhen both are specified- Invalid
maxHeightvalues automatically fall back tomaxRowsbehavior - Scrolling is automatically enabled when content exceeds the configured height
Input Border Configuration
Control the border above the input area:
input: {
// Hide the border completely
topBorder: false
}input: {
// Show default border (this is the default behavior)
topBorder: true
}input: {
// Custom border styling
topBorder: {
show: true,
color: "#005a9c", // Custom color
width: "2px" // Custom width
}
}Configuration Options:
false- Hide the border completelytrueor omit - Show the default border (gray, adapts to light/dark theme)- Object - Full customization with:
show- Whether to show the border (default:true)color- Custom border color (CSS color value)width- Border width (CSS width value, default:"1px")
Important Notes:
- By default, the border is visible with theme-based colors (light gray in light mode, dark gray in dark mode)
- Custom colors override the theme-based defaults
- The border appears above the input area, separate from the input field’s own border (configured via
borderColor) - Useful for creating visual separation or matching your brand colors
Autofocus Configuration
Control when the input field receives automatic focus:
input: {
// Focus immediately when widget loads
autofocusOnLoad: true,
// Focus after receiving bot responses (default: true)
autofocusAfterResponse: true
}Configuration Options:
autofocusOnLoad(default:false) - Whether to autofocus the input field when the widget first loads/mountsautofocusAfterResponse(default:true) - Whether to autofocus the input field after receiving a bot response
Use Cases:
Enable autofocus on load - Ideal for dedicated support pages where immediate typing is expected:
input: {
autofocusOnLoad: true
}Disable all autofocus - Essential for demo pages with multiple widgets to prevent focus conflicts:
input: {
autofocusOnLoad: false,
autofocusAfterResponse: false // User must click to focus
}Default behavior - No autofocus on load, but autofocus after responses to maintain conversational flow:
// No configuration needed - this is the default
input: {
// autofocusOnLoad: false (default)
// autofocusAfterResponse: true (default)
}Important Notes:
- Read-only mode (
advanced.readOnly: true) always disables autofocus regardless of these settings - Autofocus is automatically disabled when Text-to-Speech (TTS) is playing to prevent interrupting audio
- Mobile browsers may restrict programmatic focus based on security policies
- The default behavior preserves backward compatibility with existing implementations
Send Button Configuration
All send button styling is grouped in the sendButton sub-object for better organization:
input: {
sendButton: {
text: "Send", // Custom text (replaces default icon)
style: "filled", // "filled" (default) or "text"
color: "#007bff", // Base color
backgroundColor: "#007bff", // Background (overrides color)
textColor: "#ffffff", // Text/icon color
padding: "8px 16px", // Button padding
borderRadius: "8px", // Border radius
border: "none", // Border style
fontSize: "base", // Font size preset or CSS value
fontWeight: "semibold", // Font weight preset
fontFamily: "'Inter', sans-serif", // Font family
disabledOpacity: "0.5", // Opacity when disabled
disabledTextColor: "#999999" // Text color when disabled
}
}SendButtonConfig Properties:
| Property | Type | Description |
|---|---|---|
text | string | Custom text label (replaces default icon) |
style | "filled" | "text" | Button appearance mode (default: "filled") |
color | string | Base color (background in filled, text in text mode) |
backgroundColor | string | Override background color |
textColor | string | Text/icon color |
padding | string | CSS padding value |
borderRadius | string | Border radius (falls back to input.borderRadius) |
border | string | CSS border value |
fontSize | FontSize | Font size preset or CSS value |
fontWeight | FontWeight | Font weight preset |
fontFamily | string | Font family |
disabledOpacity | string | Opacity when disabled (default: "0.5") |
disabledTextColor | string | Text color when disabled |
Button Style Examples:
// Filled button (default)
sendButton: {
style: "filled",
color: "#3B82F6",
textColor: "#ffffff"
}
// Text-only button (transparent background)
sendButton: {
style: "text",
color: "#3B82F6",
backgroundColor: "transparent",
border: "none"
}Migration from Deprecated Properties
The flat sendButtonX properties are deprecated but still supported for backward compatibility. Here’s how to migrate:
Before (deprecated, but still supported):
input: {
sendButtonColor: "#007bff",
sendButtonText: "Send",
sendButtonStyle: "filled",
sendButtonPadding: "12px 24px",
sendButtonFontSize: "base",
sendButtonFontWeight: "semibold"
}After (recommended):
input: {
sendButton: {
color: "#007bff",
text: "Send",
style: "filled",
padding: "12px 24px",
fontSize: "base",
fontWeight: "semibold"
}
}Important Notes:
- The deprecated properties continue to work as fallbacks for backward compatibility
- TypeScript users will see deprecation warnings when using flat
sendButtonXproperties - If both old and new properties are present, the new
sendButtonproperties take precedence - New configurations should use the
sendButtonobject for better organization
Send Button Typography
Customize the appearance of send button text (when using sendButton.text instead of the default icon):
input: {
sendButton: {
text: "Send Message",
fontSize: "lg", // 18px
fontWeight: "bold", // 700
fontFamily: "'Inter', sans-serif"
}
}Font Size Options:
- Preset values:
"xs"(12px),"sm"(14px),"base"(16px),"lg"(18px),"xl"(20px) - Custom CSS values: Any valid CSS font-size value like
"14px","1.5rem","0.875em"
Font Weight Options:
- Preset values:
"light"(300),"normal"(400),"medium"(500),"semibold"(600),"bold"(700)
Important Notes:
- Font size and weight only affect text buttons (when
sendButton.textis configured) - Icon buttons (default) are unaffected by these settings - icons remain fixed at 20x20 pixels
- If not specified, button text inherits the default styling from the container
- For accessibility, we recommend a minimum of
"sm"(14px) for button text
Messages
Configure system messages:
{
messages: {
welcomeMessage: "Hello! How can I help you today?",
welcomeMessageDisplay: "static", // 'static' (default) or 'bubble'
welcomeMessageShowAvatar: true, // Show avatar on welcome bubble (only applies when welcomeMessageDisplay is 'bubble')
errorMessage: "Sorry, something went wrong. Please try again.",
offlineMessage: "We are currently offline. Please try again later.",
typingIndicatorText: "Typing...",
loadingAnimation: "typing", // 'typing', 'dots', 'pulse'
loadingAnimationColor: "#007bff",
},
}Autoscroll Configuration
Control whether the widget automatically scrolls to the bottom when new messages are added:
messages: {
// Automatically scroll to bottom when new messages arrive (default: true)
autoscroll: true
}Configuration Option:
autoscroll(default:true) - Whether to automatically scroll to the bottom when new messages are added
Use Cases:
Disable autoscroll on demo pages - Prevent unexpected scrolling when multiple widgets are on the same page:
messages: {
autoscroll: false // User controls scrolling manually
}Disable autoscroll in embedded widgets - Prevent the widget from forcing page scrolls on content pages:
messages: {
autoscroll: false // Widget won't scroll when embedded in content
}Enable autoscroll (default) - Ideal for floating widgets and dedicated chat pages:
// No configuration needed - this is the default
messages: {
// autoscroll: true (default)
}Important Notes:
- Read-only mode (
advanced.readOnly: true) always disables autoscroll regardless of this setting - The default behavior preserves backward compatibility with existing implementations
- Particularly useful for demo pages with multiple widgets to prevent scroll conflicts
Link Styling
Customize the appearance of links in all message content:
{
messages: {
links: {
// Link text color (CSS color value)
// Default: browser default link color
color: "#0066cc",
// Hover state color (CSS color value)
// If not specified, falls back to the same color as 'color'
// Default: same as color
hoverColor: "#0052a3",
// Whether to underline links
// Default: true
underline: true,
// Whether links should open in a new tab
// Default: true
openInNewTab: true,
// Font weight for link text
// Options: 'light' | 'normal' | 'medium' | 'semibold' | 'bold'
// Default: inherited from parent styles
fontWeight: "bold",
},
},
}Important Notes:
- Link styling applies to all links in markdown content across all message types (assistant, agent, user, events, welcome messages, etc.)
- Ensure sufficient color contrast between link colors and message bubble backgrounds for accessibility
- WCAG AA standard recommends a contrast ratio of at least 4.5:1 for normal text, 3:1 for large text
- All link styling options are optional - if not specified, browser defaults are used
Font Size Configuration
Control font sizes throughout the widget using the fontSize property available on multiple configuration objects. The widget supports both preset size values and custom CSS values.
Global Base Font Size
Set a base font size for the entire widget:
typography: {
baseFontSize: "base" // 16px (default)
// or
baseFontSize: "14px" // Custom pixel value
// or
baseFontSize: "1.25em" // Custom em value
}Component-Specific Font Sizes
Override the base font size for specific components:
Header Title:
window: {
header: {
fontSize: "lg", // 18px
fontFamily: "'Roboto', sans-serif"
}
}Message Bubbles (per role):
bubbles: {
user: {
fontSize: "base", // 16px
backgroundColor: "#2563eb",
textColor: "#ffffff"
},
assistant: {
fontSize: "sm", // 14px
backgroundColor: "#f3f4f6",
textColor: "#111827"
},
agent: {
fontSize: "15px", // Custom value
backgroundColor: "#16a34a",
textColor: "#ffffff"
}
}Input Field:
input: {
fontSize: "base", // 16px for textarea
placeholder: "Type your message...",
fontFamily: "'Inter', sans-serif"
}Send Button Text:
input: {
sendButtonText: "Send",
sendButtonFontSize: "base", // 16px
sendButtonFontWeight: "semibold", // 600
sendButtonFontFamily: "'Inter', sans-serif"
}Links:
messages: {
links: {
fontSize: "sm", // 14px
fontWeight: "semibold",
color: "#005a9c"
}
}Preset Font Sizes
The widget provides the following preset size values that map to standard Tailwind sizes:
| Preset | CSS Value | Pixels |
|---|---|---|
"xs" | 12px | 12px |
"sm" | 14px | 14px |
"base" | 16px | 16px |
"lg" | 18px | 18px |
"xl" | 20px | 20px |
Custom CSS Values
You can also use any valid CSS font-size value:
- Pixels:
"14px","18px"(recommended - consistent across all sites) - Rem:
"1.25rem","0.875rem"(relative to document root - for accessibility) - Em:
"1.5em","0.9em"(relative to parent element) - Percentages:
"90%","110%" - Other units:
"1.2vw","16pt"
Important Note on Accessibility
The widget’s preset sizes now use px units for consistent appearance across different websites. This provides predictable sizing but does not scale with user browser font-size preferences.
If you want your widget to respect user accessibility settings (larger default fonts), you can:
- Use rem or em units in custom CSS values:
fontSize: "1rem" - Set
typography.baseFontSizeto a rem value:baseFontSize: "1rem"
Trade-offs:
- px units (recommended): Consistent appearance across all sites, but ignores user font-size preferences
- rem/em units (for accessibility): Respects user accessibility settings, but may appear different sizes on different sites depending on the host page’s root font size
Complete Font Size Example
const config = {
experienceId: "your-experience-id",
apiUrl: "https://nexus-api.uat.knowbl.com/api/v2",
// Global base font size
typography: {
baseFontSize: "base", // 16px - sets the root size
},
window: {
header: {
fontSize: "lg", // 18px header title
fontFamily: "'Roboto', sans-serif",
},
},
bubbles: {
user: {
fontSize: "base", // 16px for user messages
backgroundColor: "#2563eb",
textColor: "#ffffff",
},
assistant: {
fontSize: "sm", // 14px for assistant messages
backgroundColor: "#f3f4f6",
textColor: "#111827",
},
agent: {
fontSize: "15px", // Custom 15px for agent messages
backgroundColor: "#16a34a",
textColor: "#ffffff",
},
},
input: {
fontSize: "base", // 16px for input field
placeholder: "Ask a question...",
fontFamily: "'Roboto', sans-serif",
sendButtonText: "Send",
sendButtonFontSize: "base", // 16px for send button text
sendButtonFontWeight: "semibold", // 600 weight for emphasis
},
messages: {
links: {
fontSize: "sm", // 14px for links
fontWeight: "semibold",
color: "#005a9c",
},
},
};
Important Notes:
- If
fontSizeis not specified for a component, it inherits the base font size fromtypography.baseFontSizeor the browser default (typically 16px) - Component-specific
fontSizesettings always override the globalbaseFontSize - Font sizes are protected from host page CSS overrides using high-specificity selectors and
!importantdeclarations - The widget ensures consistent font rendering across all browsers and hostile CSS environments
Markdown Typography Sizes
By default, all markdown elements (headings, code blocks, inline code) inherit the font size from their parent bubble. However, you can enable semantic typography sizing to create visual hierarchy:
messages: {
markdown: {
applyTypographySizes: true // Enable semantic sizing
}
}When enabled (true), markdown elements use these sizes:
<h1>: xl (20px)<h2>: lg (18px)<h3>: base (16px)<h4>,<h5>,<h6>: sm (14px)<code>and code blocks: sm (14px)
When disabled (false, default):
- All markdown elements inherit the bubble’s
fontSizeconfiguration - Headings remain bold and maintain margins for visual distinction
- Provides consistent font sizing across all message content
Welcome Message Bubble
Display the welcome message as an assistant message bubble instead of static text:
{
messages: {
// Display welcome message as an assistant bubble
welcomeMessage:
"👋 **Welcome!** I'm your AI assistant. How can I help you today?",
welcomeMessageDisplay: "bubble", // Show as assistant message bubble
welcomeMessageShowAvatar: true, // Show avatar on welcome bubble
},
bubbles: {
assistant: {
showAvatar: true, // Enable avatars for assistant messages
avatarUrl: "https://example.com/avatar.png", // Optional custom avatar
},
},
}Display Modes:
"static"(default) - Shows welcome message as gray text without bubble styling"bubble"- Displays as an assistant message bubble with optional avatar
Key Features:
- Supports full markdown formatting (bold, italic, links, lists, etc.)
- Interactive action buttons for guiding users (see Welcome Message Actions below)
- Shows/hides avatar based on
welcomeMessageShowAvatarconfiguration - Inherits all assistant bubble styling (colors, border radius, etc.)
- Appears when chat is empty (no messages)
- Disappears when user sends first message (unless persistent mode is enabled)
- Reappears when chat is cleared
- Does not show timestamp, copy button, or feedback buttons
- Not saved in session history or turn counting
Avatar Display Logic:
The welcome bubble shows an avatar only when both conditions are true:
welcomeMessageShowAvataristrue(defaults totrue)bubbles.assistant.avatar.enabledistrue
This allows you to independently control avatar display for the welcome bubble while maintaining avatar settings for regular assistant messages.
Example - Bubble without Avatar:
const config = {
messages: {
welcomeMessage: "Welcome! Ask me anything.",
welcomeMessageDisplay: "bubble",
welcomeMessageShowAvatar: false, // Hide avatar on welcome bubble only
},
bubbles: {
assistant: {
avatar: {
enabled: true, // Still show avatar on regular messages
},
},
},
};Persistent Welcome Messages
By default, the welcome message disappears when the user sends their first message. You can make it persist throughout the entire conversation:
const config = {
messages: {
welcomeMessage: "👋 Welcome! I'm here to help.",
welcomeMessageDisplay: "bubble",
welcomeMessagePersistent: true, // Keep welcome visible throughout conversation
},
};Persistent Mode Behavior:
- Welcome message appears after the session start event
- Remains visible as users send messages and receive responses
- Preserved when chat is cleared and reappears after new session starts
- Automatically excluded from message counts and analytics
- Works with both
"static"and"bubble"display modes
Use Cases:
- Display important instructions or guidelines throughout the conversation
- Show contact information or support links that users may need at any time
- Create a consistent header or introduction that stays visible
- Provide context or disclaimers that should always be accessible
Important Notes:
- Welcome bubble is a synthetic UI element and does not count as a conversation message
- Ideal for creating a more natural, conversational first impression
- When
welcomeMessagePersistent: false(default), the welcome message uses the traditional behavior (disappears after first interaction)
Welcome Message Actions
Add interactive action buttons to your welcome message bubble to guide users and provide quick options:
const config = {
messages: {
welcomeMessage: "👋 Hello! What would you like to do?",
welcomeMessageDisplay: "bubble", // Actions only work in bubble mode
welcomeActions: {
alignment: "center", // "left" | "center" | "right"
spacing: "0.5rem", // Gap between buttons
position: "bottom", // Always below message
actions: [
{
id: "product-info",
label: "Product Info",
action: {
type: "send_query",
queryText: "Tell me about your products",
},
},
{
id: "support",
label: "Get Support",
action: {
type: "trigger_handover",
},
behavior: {
oneTimeUse: true, // Disable after click
},
},
{
id: "learn-more",
label: "Learn More",
action: {
type: "open_url",
url: "https://example.com/help",
},
},
],
},
},
};Action Types:
send_query- Automatically sends a query to the assistanttrigger_handover- Initiates agent handover (if configured)open_url- Opens a URL in new tabsend_event- Emits custom event for your app to handle
Key Features:
- Actions only appear in bubble display mode (not static mode)
- Configurable alignment (left, center, right) and spacing
- One-time-use behavior disables button after click
- Action states persist across session transitions and chat resets
- Inherits button styling from action button configuration
Important Notes:
- Welcome actions are only supported when
welcomeMessageDisplay: "bubble" - Action state persistence works with both persistent and non-persistent welcome messages
- For non-persistent welcome messages, action states reset when the welcome message disappears
Example - Persistent Welcome with Query Actions:
const config = {
messages: {
welcomeMessage: "👋 **Need help?** Choose a topic:",
welcomeMessageDisplay: "bubble",
welcomeMessagePersistent: true, // Keep visible throughout conversation
welcomeActions: {
alignment: "left",
actions: [
{
id: "getting-started",
label: "Getting Started",
action: {
type: "send_query",
queryText: "How do I get started?",
},
},
{
id: "features",
label: "Features",
action: {
type: "send_query",
queryText: "What features are available?",
},
},
{
id: "pricing",
label: "Pricing",
action: {
type: "send_query",
queryText: "Tell me about pricing",
},
},
],
},
},
};This creates a persistent help menu that stays visible throughout the conversation, allowing users to quickly ask about common topics at any time.
See the Welcome Message Demo for live examples.
Scrollbar Styling
Customize the appearance and behavior of the message container scrollbar using a JavaScript-based custom scrollbar implementation that works consistently across all browsers:
{
scrollbar: {
// Width of the scrollbar (e.g., "8px", "12px", "thin")
// Works consistently across all browsers
// Default: browser default
width: "8px",
// Background color of the scrollbar track
// Works consistently across all browsers
// Default: browser default
trackColor: "#f1f1f1",
// Color of the scrollbar thumb
// Works consistently across all browsers
// Default: browser default
thumbColor: "#888",
// Color of the scrollbar thumb on hover
// Works consistently across all browsers
// Default: thumbColor or browser default
thumbHoverColor: "#555",
// Border radius of the scrollbar thumb
// Works consistently across all browsers
// Default: browser default
borderRadius: "4px",
// Enable auto-hide behavior
// When enabled, scrollbar automatically hides after inactivity
// Works consistently across all browsers via JavaScript implementation
// Default: false
autoHide: true,
// Delay in milliseconds before auto-hiding (only applies when autoHide is enabled)
// Default: 1000
autoHideDelay: 1000,
},
}Browser Support:
- AutoHide: Fully functional across all supported browsers
- Styling: All color, width, and border-radius options work consistently across browsers
Important Notes:
- The widget uses a JavaScript-based custom scrollbar to ensure consistent behavior across all browsers and customer environments
- AutoHide functionality is no longer affected by customer site CSS specificity conflicts
- All configuration options work reliably in all supported browsers
- All properties are optional. When scrollbar config is not provided, browser default scrollbar is used
Event Messages
Customize messages for specific events:
{
events: {
sessionStart: {
message: "Chat session started",
show: true,
},
sessionEnd: {
message: "Chat session ended",
show: true,
},
agentHandover: {
message: "Connecting you to a live agent...",
show: true,
},
},
}Event Layout
Configure horizontal layout for system events (session start, agent handover, session end):
// Horizontal event layout configuration
const config = {
events: {
// Session start with horizontal layout
sessionStart: {
enabled: true,
showBadge: false, // Don't show badge pill in horizontal mode
showTimestamp: true, // Show timestamp on right
layout: "horizontal", // NEW: Horizontal layout
message: "You started a chat with **Acme Support**", // Markdown with bold
messageFormat: "markdown",
},
// Agent handover with horizontal layout
agentHandover: {
enabled: true,
showCard: false, // Use simple mode
showTimestamp: true, // Show timestamp on right
layout: "horizontal", // NEW: Horizontal layout
message: "Connecting you with a live agent...",
},
// Session end with horizontal layout
sessionEnd: {
enabled: true,
showBadge: false, // Don't show badge pill in horizontal mode
showTimestamp: true, // Show timestamp on right
showDuration: true, // Show duration inline
layout: "horizontal", // NEW: Horizontal layout
message: "Chat session ended",
messageFormat: "text",
},
},
// Window with top accent
window: {
topAccentColor: "#005a9c", // Blue bar at top
topAccentHeight: "4px",
header: {
backgroundColor: "#ffffff",
textColor: "#1a1a1a",
},
},
};This layout displays event messages on the left with timestamps right-aligned, creating a cleaner, more professional appearance. Works great with markdown-formatted messages.
Event Styling
All event types (sessionStart, sessionEnd, agentHandover) support styling configuration. You can set default styles that apply to all events, with optional per-type overrides.
Default Event Styling
Apply consistent styling to all events:
KnowblWidget.init({
events: {
default: {
fontSize: "sm", // Font size preset or custom CSS value
fontFamily: "Arial, sans-serif",
textColor: "#374151", // Custom text color
backgroundColor: "#F9FAFB", // Custom background color
borderRadius: "8px", // Border radius
padding: "16px", // Padding (CSS shorthand)
},
},
});Per-Type Overrides
Override default styles for specific event types:
KnowblWidget.init({
events: {
default: {
fontSize: "sm",
textColor: "#374151",
},
sessionStart: {
fontSize: "base", // Override: larger font for session start
backgroundColor: "#DBEAFE", // Override: blue background
},
sessionEnd: {
backgroundColor: "#FEE2E2", // Override: red background
},
// agentHandover uses default styles
},
});Available Styling Properties
fontSize: Font size (preset: ‘xs’, ‘sm’, ‘base’, ‘lg’, ‘xl’, ‘2xl’, ‘3xl’, ‘4xl’ or custom CSS value like ‘14px’)fontFamily: Font family (CSS font-family value)fontStyle: Font style (‘normal’, ‘italic’, or ‘oblique’)textColor: Text color (any valid CSS color)backgroundColor: Background color (any valid CSS color)borderRadius: Border radius (CSS border-radius value)padding: Padding (CSS padding shorthand)
Disclaimer
Add a disclaimer or terms of service. Three display modes are available:
- Footer (default): Collapsible section at the bottom of the widget
- Modal: Blocking gate that must be accepted before chatting
- Event: Inline disclaimer in the message stream, optionally blocking input
// Footer mode (default) - collapsible section at bottom
const footerConfig = {
disclaimer: {
content:
"⚠️ **Disclaimer**: This chatbot provides general information only.",
contentFormat: "markdown",
collapsible: true,
defaultExpanded: false,
},
};
// Modal mode - blocking gate before chat
const modalConfig = {
disclaimer: {
required: true,
requiredTitle: "Terms of Use",
requiredAcceptButtonText: "I Agree & Continue",
content: "**Terms**: You must accept before chatting...",
contentFormat: "markdown",
},
};
// Event mode - inline in message stream
const eventConfig = {
disclaimer: {
displayMode: "event",
content: "**Notice**: This AI provides general information only.",
contentFormat: "markdown",
eventRequiresAcceptance: true, // Block input until accepted
acceptButtonText: "I Accept",
},
// Optional: customize event styling
events: {
disclaimer: {
backgroundColor: "transparent", // or "none" to remove
borderColor: "none",
showIcon: false,
showAcknowledged: false,
},
},
};Acceptance is persisted to localStorage per experience ID. The disclaimerAccept event is emitted when users accept any disclaimer type.
See the Disclaimer Demo for interactive examples of all modes.
Branding
Control powered-by branding:
{
branding: {
show: false, // Show "Powered by Nexus" branding
text: "Powered by Nexus", // Custom branding text
url: "https://your-company.com", // Link URL
},
}Features
Enable or disable features:
{
features: {
// Voice input
voice: {
enabled: true, // Enable voice input
language: "en-US", // Voice recognition language
},
// Text-to-speech
tts: {
enabled: false, // Enable TTS
defaultOn: false, // Start with TTS on
synthesisConfig: {
engine: "browser", // 'browser' or 'aws'
voiceId: "default",
rate: 1.0, // Speech rate (0.5 - 2.0)
pitch: 1.0, // Speech pitch (0.5 - 2.0)
volume: 1.0, // Volume (0.0 - 1.0)
},
},
// File upload
fileUpload: false, // Enable file attachments
// Emoji picker
emoji: false, // Enable emoji picker
// Message format
format: "markdown", // 'markdown' or 'html'
// Copy button
copyButton: true, // Show copy button on messages
// Feedback system
feedback: {
enabled: true, // Enable feedback
style: "thumbs", // 'thumbs', 'stars', or 'emoji'
position: "inline", // 'inline' or 'modal'
positive: {
label: "Helpful", // Custom label
},
negative: {
label: "Not helpful",
requireComment: true, // Require comment for negative feedback
},
},
// Clear Conversation menu option
clearConversation: {
enabled: true, // Enabled by default unless set to false
label: "Start Over", // Custom label text
},
},
}Advanced Configuration
Advanced options for special use cases:
{
advanced: {
// User identification
userId: "unique-user-id", // Identify users across sessions
// Session management
sessionId: "550e8400-e29b-41d4-a716-446655440000", // Resume existing session (must be UUID v4)
// Widget ID
widgetId: "custom-widget-id", // Custom widget identifier
// Store persistence
persistStore: true, // Remember conversations
storeKey: "nexus-chat-session", // Storage key
// Storage limits
maxStoredEvents: 500, // Max events in localStorage (default: 500, min: 10, max: 2000)
// Initial events (pre-populate conversation)
initialEvents: [
{
kind: "message",
role: "assistant",
content: "Welcome back! How can I help you?",
timestamp: new Date(),
},
],
// Read-only mode
readOnly: false, // Disable user input
// Message interceptor
messageInterceptor: async (message, context) => {
// Custom message handling
return null; // or return custom response
},
// Interceptor timeout
interceptorTimeout: 5000, // Timeout in milliseconds
// Custom HTTP headers for all API requests
headers: {
"X-Correlation-ID": "abc-123-def-456",
"X-Customer-ID": "customer-789",
},
// Custom protocol handlers for action links
customProtocolHandlers: {
"walkme:": (params) => {
if (params.flowId && window.WalkMeAPI) {
window.WalkMeAPI.startFlowById(parseInt(params.flowId));
}
},
},
},
}Custom HTTP Headers
Pass custom headers to all API requests made by the widget:
{
advanced: {
// Custom HTTP headers included in all API requests
headers: {
// Distributed tracing
"X-Correlation-ID": crypto.randomUUID(),
// Customer identification
"X-Customer-ID": "customer-456",
// Custom authentication for proxy infrastructure
"X-Custom-Auth": "custom-token-xyz",
// Request source tracking
"X-Request-Source": "mobile-app",
},
},
}How It Works:
Custom headers are included in all API requests made by the widget.
Important Notes:
- Built-in headers (
Content-Type,Authorization) cannot be overridden - If you provide headers with these names, the built-in values take precedence
- Headers must be string key-value pairs (
Record<string, string>)
Use Cases:
| Use Case | Example Header |
|---|---|
| Distributed tracing | "X-Correlation-ID": crypto.randomUUID() |
| Proxy authentication | "X-Custom-Auth": "bearer-token" |
| Customer identification | "X-Customer-ID": "cust-123" |
| Analytics tracking | "X-Request-Source": "mobile-app" |
Dynamic Headers Example:
const config = {
advanced: {
headers: {
// Generate new correlation ID for each page load
"X-Correlation-ID": crypto.randomUUID(),
// Include user context from your application
"X-User-Context": JSON.stringify({
page: window.location.pathname,
referrer: document.referrer
})
}
}
};Cross-Origin Credentials
Control whether cookies and HTTP authentication are sent with cross-origin API requests:
const config = {
advanced: {
// Send credentials with all API requests (query, feedback, TTS)
credentials: "include"
}
};Available Values:
| Value | Description |
|---|---|
"omit" | Never send credentials |
"same-origin" | Only send credentials for same-origin requests (browser default) |
"include" | Always send credentials, including for cross-origin requests |
CORS Server Requirements:
When using credentials: "include", your backend API must respond with appropriate CORS headers:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://your-website.com // Must be specific origin, not *Use Cases:
| Use Case | Configuration |
|---|---|
| Cross-origin cookie auth | credentials: "include" |
| Session cookies across domains | credentials: "include" |
| API behind auth proxy | credentials: "include" |
| Standard same-origin API | Omit (uses browser default) |
Example - Cross-Origin Authentication:
// Widget on https://chat.example.com
// API on https://api.example.com
const config = {
experienceId: "your-experience-id",
apiUrl: "https://api.example.com/api",
advanced: {
credentials: "include" // Sends cookies from api.example.com
}
};Important Notes:
- Default behavior (when not specified) is
"same-origin"- cookies are only sent for same-origin requests - All three widget API endpoints respect this setting: query, feedback, and TTS synthesis
- Using
"include"with a wildcardAccess-Control-Allow-Origin: *on the server will cause CORS errors - This setting is independent of the
headersconfiguration - both can be used together
Custom Protocol Handlers
Automatically handle clicks on links with custom protocols (e.g., walkme:, action:, tour:). This provides a declarative alternative to listening for linkClick events.
const widget = window.NexusChatWidget.init({
experienceId: "your-experience-id",
apiUrl: "https://api.example.com",
advanced: {
customProtocolHandlers: {
// Handle WalkMe tour links
"walkme:": (params, event) => {
const flowId = params.flowId || params.value;
if (flowId && window.WalkMeAPI) {
window.WalkMeAPI.startFlowById(parseInt(flowId));
}
},
// Handle custom action links
"action:": (params, event) => {
switch (params.type) {
case "schedule":
openSchedulingModal(params);
break;
case "download":
initiateDownload(params.fileId);
break;
}
},
// Handle product links
"product:": (params, event) => {
openProductModal(params.id || params.value);
}
}
}
});How It Works:
When a user clicks a link with a custom protocol in an AI response:
- The
linkClickevent is emitted (for analytics/tracking) - If a matching handler exists, it’s called with the parsed parameters
- Default browser navigation is prevented for custom protocols
Handler Parameters:
| Parameter | Type | Description |
|---|---|---|
params | Record<string, string> | Parsed parameters from the URL (e.g., flowId=123) |
event | LinkClickEventData | Full event data including href, text, sessionId |
URL Parsing Examples:
| Link | Protocol | Params |
|---|---|---|
walkme:flowId=123456 | walkme: | { flowId: "123456" } |
walkme:123456 | walkme: | { value: "123456" } |
action:type=schedule&id=abc | action: | { type: "schedule", id: "abc" } |
When to Use:
| Approach | Best For |
|---|---|
customProtocolHandlers | Simple, declarative protocol-to-action mapping |
linkClick event listener | Analytics, logging, complex conditional logic |
Both approaches can be used together - the event is always emitted, and handlers are called afterward.
Session Management
The widget provides flexible session management options:
Initialize with Existing Session
You can initialize the widget with an existing session ID to resume a previous conversation:
const widget = window.NexusChatWidget.init({
experienceId: "your-experience-id",
apiUrl: "https://nexus-api.uat.knowbl.com/api/v2",
advanced: {
sessionId: "550e8400-e29b-41d4-a716-446655440000", // Must be valid UUID v4
},
});
Requirements:
- SessionId must be a valid UUID v4 format
- Can be generated client-side or obtained from the Create Session API (
POST /api/v2/sessions) - If the session doesn’t exist on the backend, it will be created
- If messages exist in the store for this session, they will be displayed
- If no messages exist, a new session starts with the provided ID
Use Cases:
- Resume session after page reload (store sessionId in localStorage)
- Coordinate multiple widgets sharing the same session
- Link widget session to your application’s user session
- Initialize widget with a session created via API (server-side session management)
Programmatic Session Control
Create new sessions programmatically using the startNewSession() method:
// Start a new session with auto-generated UUID
const sessionId = widget.startNewSession();
console.log("New session started:", sessionId);
// Or start with a specific session ID
const customSessionId = widget.startNewSession(
"550e8400-e29b-41d4-a716-446655440000",
);Behavior:
- Ends the current session (emits
sessionEndevent) - Clears all messages and events
- Starts a new session (emits
sessionStartevent) - Displays the welcome message (if configured in
messages.welcomeMessage) - Returns the new session ID
- Throws error if provided sessionId is not a valid UUID
Session Events
Track session lifecycle with events:
widget.on("sessionStart", (event) => {
console.log("Session started:", event.sessionId);
// Store sessionId for persistence
localStorage.setItem("chatSessionId", event.sessionId);
});
widget.on("sessionEnd", (event) => {
console.log("Session ended:", event.sessionId);
console.log("Duration:", event.duration, "ms");
});API-to-Widget Session Flow
Create a session via API first, then initialize the widget with it:
// Server-side: Create session via API
const response = await fetch("https://nexus-api.uat.knowbl.com/api/api/v2/sessions", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer YOUR_ACCESS_TOKEN",
},
body: JSON.stringify({
experienceId: "your-experience-id",
userId: "user@example.com", // Optional
metadata: {
source: "web-app",
plan: "premium",
},
}),
});
const { id: sessionId } = await response.json();
// Client-side: Initialize widget with the session
const widget = window.NexusChatWidget.init({
experienceId: "your-experience-id",
apiUrl: "https://nexus-api.uat.knowbl.com/api/v2",
advanced: {
sessionId: sessionId, // Use the session created via API
},
});
This pattern is useful for:
- Server-side session creation with metadata
- Pre-associating sessions with user accounts
- Implementing custom session management logic
Session Continuity Example
Implement session persistence across page reloads:
// On page load, check for stored session
const storedSessionId = localStorage.getItem("chatSessionId");
const widget = window.NexusChatWidget.init({
experienceId: "your-experience-id",
apiUrl: "https://nexus-api.uat.knowbl.com/api/v2",
advanced: {
sessionId: storedSessionId || undefined, // Resume if available
},
});
// Store new sessions for future page loads
widget.on("sessionStart", (event) => {
localStorage.setItem("chatSessionId", event.sessionId);
});
// Clear stored session when user explicitly ends it
widget.on("sessionClear", (event) => {
localStorage.removeItem("chatSessionId");
});
Tab Synchronization
The widget automatically synchronizes chat state across multiple browser tabs. When a user has your site open in multiple tabs, their chat session, messages, and widget state stay in sync.
How It Works
When tab synchronization is enabled (the default), the widget:
- Shares the active session - Opening or continuing a conversation in one tab makes it available in all tabs
- Syncs messages in real-time - Messages sent or received in one tab appear instantly in other tabs
- Coordinates widget state - Opening or closing the widget in one tab reflects in all tabs
The synchronization uses the browser’s BroadcastChannel API with automatic fallback to localStorage events for older browsers.
What Is Synchronized
| Synchronized (shared across tabs) | Not Synchronized (per-tab) |
|---|---|
| Session ID | Input field text |
| Chat messages and events | Text-to-speech toggle |
| Widget open/closed state | Window size mode |
| Loading states | TTS playback state |
Disabling Tab Sync
For applications where isolated widget instances are preferred:
{
// Tab synchronization is enabled by default
// Set disableTabSync to true to disable cross-tab state sharing
disableTabSync: true,
}When to disable tab synchronization:
- Multi-context applications - Different pages serve different purposes (e.g., sales page vs. support page)
- Testing environments - Isolated testing of widget behavior without cross-tab interference
- Multi-tenant dashboards - Users may have different accounts/workspaces open in different tabs
- Privacy requirements - Prevent accidental state sharing between tabs
When to keep tab sync enabled (default):
- Customer support chat - Users expect the same conversation across tabs
- E-commerce sites - Consistent chat experience while browsing products
- SaaS applications - Single conversation context per user session
- General websites - Seamless user experience without duplicate conversations
Storage Management
The widget stores chat history in localStorage to persist conversations across page reloads and synchronize across browser tabs. You can configure storage limits and programmatically manage stored data.
Configuring Storage Limits
Control how many events (messages, session events, etc.) are stored in localStorage:
const config = {
advanced: {
// Maximum events stored in localStorage
// Older events are automatically pruned when limit is exceeded
maxStoredEvents: 500 // Default: 500, Min: 10, Max: 2000
}
};Configuration Options:
| Option | Type | Default | Min | Max | Description |
|---|---|---|---|---|---|
maxStoredEvents | number | 500 | 10 | 2000 | Maximum events to store in localStorage |
Behavior:
- When the limit is exceeded, the oldest events are automatically removed (pruned)
- Most recent events are always preserved
- Values below 10 are clamped to 10
- Values above 2000 are clamped to 2000
- Session data is separate and not affected by this limit
Use Cases:
| Scenario | Recommended Limit | Rationale |
|---|---|---|
| High-volume support | 100-200 | Reduce localStorage usage |
| Long conversations | 500-1000 | Preserve more context |
| Memory-constrained devices | 50-100 | Minimize storage footprint |
| Debug/testing | 10-50 | Quick iteration |
Clearing Events Programmatically
Use the clearStoredEvents() method to clear chat history while keeping the session active. You can clear all events, keep a specific number of recent events, or clear events before a specific date.
// Get widget instance
const widget = NexusChatWidget.init(config);
// Clear all stored events (keeps session active)
widget.store.clearStoredEvents();
// Clear events but keep the last 10 messages
widget.store.clearStoredEvents(10);
// Clear events older than 24 hours
const oneDayAgo = Date.now() - 24 _ 60 _ 60 * 1000;
widget.store.clearStoredEvents({ beforeDate: oneDayAgo });
// Clear events before a specific date using ISO string
widget.store.clearStoredEvents({
beforeDate: '2026-01-01T00:00:00.000Z'
});
// Clear events before a date, but keep at least 5 messages
widget.store.clearStoredEvents({
beforeDate: new Date('2026-01-01'),
keepCount: 5
});Method Signature:
store.clearStoredEvents(options?: number | ClearEventsOptions): void
interface ClearEventsOptions {
keepCount?: number; // Keep at least N most recent events
beforeDate?: Date | string | number; // Clear events before this date
}Parameters:
| Parameter | Type | Description |
|---|---|---|
options | number | ClearEventsOptions | Number of events to keep, or options object |
options.keepCount | number | Keep at least N most recent events |
options.beforeDate | Date | string | number | Clear events with timestamps before this date |
Behavior:
- When called with no arguments: clears all stored events
- When called with a number: keeps the last N events (backward compatible)
- When
beforeDateis specified: clears events withtimestamp < beforeDate - When both
keepCountandbeforeDateare specified:- First removes events before
beforeDate - Then if remaining events exceed
keepCount, keeps only the lastkeepCount
- First removes events before
- Date parameter accepts:
Dateobjects, ISO 8601 strings, or Unix timestamps (milliseconds) - Events with timestamps equal to or after
beforeDateare preserved
Key Features:
- Session remains active after clearing
- Changes sync across all tabs automatically
- Persistent welcome messages and disclaimers are preserved
- New messages can be added immediately after clearing
- This clears events from localStorage only; it does not affect the backend
Example - Memory Management:
// Monitor and manage storage periodically
const widget = NexusChatWidget.init({
experienceId: "your-experience-id",
apiUrl: "https://api.example.com",
advanced: {
maxStoredEvents: 200 // Automatic pruning at 200
}
});
// Manual pruning when needed (e.g., user requests "clear history")
document.getElementById("clear-history").addEventListener("click", () => {
widget.store.clearStoredEvents();
console.log("Chat history cleared");
});
// Partial clear - keep recent context
document.getElementById("trim-history").addEventListener("click", () => {
widget.store.clearStoredEvents(20); // Keep last 20 messages
console.log("Older messages removed");
});Example - Time-Based Clearing:
// Clear old conversations on page load
const widget = NexusChatWidget.init(config);
// Clear messages older than 7 days
const sevenDaysAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;
widget.store.clearStoredEvents({ beforeDate: sevenDaysAgo });
// Clear messages older than a specific date, but keep at least 10
widget.store.clearStoredEvents({
beforeDate: new Date('2026-01-01'),
keepCount: 10
});Clearing All Storage Data
Use the static clearStorage() method to completely remove all localStorage data for an experience without instantiating a widget:
// Clear all localStorage data for an experience
// This removes: session, events, state, disclaimer acceptance
const success = NexusChatWidget.clearStorage("your-experience-id");
if (success) {
console.log("Storage cleared successfully");
// Widget will start fresh on next initialization
}Method Signature:
NexusChatWidget.clearStorage(experienceId: string): booleanReturns: true if storage was cleared, false if localStorage is unavailable.
What Gets Cleared:
- Session ID
- All stored events/messages
- Widget state (open/closed)
- Disclaimer acceptance status
- Any active tab coordinators
Use Cases:
- Debugging: Start fresh during development
- User logout: Clear chat data when user signs out
- Privacy compliance: Allow users to delete their chat history
- Admin tools: Provide support staff with reset capability
- Testing: Reset state between test runs
Example - Logout Integration:
// Clear chat data when user logs out
async function handleLogout() {
// Your logout logic
await api.logout();
// Clear widget storage
NexusChatWidget.clearStorage("your-experience-id");
// Redirect to login
window.location.href = "/login";
}Important Notes:
- This method works before or after widget initialization
- Running widgets are not automatically updated - destroy and recreate them after clearing
- The method is available as
NexusChatWidget.clearStorage()in the global namespace
Embedded Mode
Special configuration for embedded widgets:
{
embedded: true, // Enable embedded mode
container: document.getElementById("chat-container"), // Container element
// In embedded mode, these are typically different:
window: {
borderRadius: "0", // No rounded corners
boxShadow: "none", // No shadow
header: {
showCloseButton: false, // No close button
showSizeToggle: false, // No size toggle
},
},
}Container Requirements
When using embedded mode, your container must have explicit height constraints. The widget uses height: 100% with flex layout internally, which only works correctly when the parent container has a defined height.
Why this matters: The widget’s input area is positioned at the bottom using flex layout (not position: fixed). Without a height constraint on the container, the widget collapses to its content height and the input won’t appear “fixed” at the bottom.
Symptoms of missing height:
- Input area appears at the bottom of content, not the bottom of the container
- Widget height equals its content height instead of filling the container
- Especially noticeable with Shadow DOM + headerless configuration
Valid container patterns:
/* 1. Fixed height */
.container {
height: 500px;
}
/* 2. Viewport height */
.container {
height: 100vh;
}
/* 3. Partial viewport with calc */
.container {
height: calc(100vh - 60px); /* Minus header */
}
/* 4. Flex child with parent height */
.parent {
display: flex;
flex-direction: column;
height: 100vh;
}
.container {
flex: 1;
min-height: 0; /* Important for flex shrinking */
}
/* 5. Absolute positioning */
.container {
position: absolute;
top: 60px;
bottom: 0;
left: 0;
right: 0;
}
/* 6. Grid layout */
.parent {
display: grid;
grid-template-rows: auto 1fr;
height: 100vh;
}
.container {
/* Automatically fills grid cell */
}Invalid patterns (will cause issues):
/* These will NOT work properly */
.container {
height: auto; /* No constraint */
}
.container {
/* No height at all */
}
.container {
height: 100%; /* Only works if parent has height */
}Debug tip: If the input isn’t at the bottom, inspect your container element. If container.clientHeight === container.scrollHeight, the container lacks a height constraint and is sizing to its content.
Console warning: The widget will log a warning to the console if it detects that the container may be missing height constraints. This helps developers identify configuration issues during development.
Shadow DOM Mode
Enable Shadow DOM encapsulation for complete CSS isolation. This is useful when embedding the widget on pages with aggressive CSS that might interfere with widget styling.
// Enable Shadow DOM for complete CSS isolation
const config = {
experienceId: "your-experience-id",
apiUrl: "{{API_URL}}/v2",
// Enable Shadow DOM encapsulation
useShadowDOM: true,
// Other configuration options work normally
embedded: true,
window: {
header: {
title: "Chat Support",
},
},
};
// Initialize with Shadow DOM enabled
NexusChatWidget.init(config, document.getElementById("widget-container"));Live Demo
See the difference between Shadow DOM enabled and disabled in a hostile CSS environment:
When to use Shadow DOM:
- Embedding on pages with global CSS resets that affect all elements
- Sites with CSS frameworks that use high-specificity selectors
- Environments where host page styles conflict with widget appearance
- When you need guaranteed visual consistency regardless of host page styles
How it works:
When useShadowDOM: true is set, the widget:
- Creates a Shadow DOM boundary around the widget content
- Injects all widget CSS into the shadow root (isolated from host page)
- Prevents host page CSS from affecting widget elements
- Prevents widget CSS from leaking to the host page
Trade-offs:
| Benefit | Consideration |
|---|---|
| Complete CSS isolation | Some browser dev tools show shadow DOM differently |
| Protection from hostile CSS | Third-party analytics tools may not see widget DOM |
| Consistent appearance everywhere | Inherited CSS properties still cascade (color, font-family) |
| No selector conflicts | Slightly larger memory footprint |
Default behavior:
By default, useShadowDOM is false. The widget uses high-specificity selectors and !important declarations to resist most CSS conflicts. Shadow DOM mode provides stronger isolation for challenging environments.
Browser Support:
Shadow DOM is supported in all modern browsers:
- Chrome 53+
- Firefox 63+
- Safari 10+
- Edge 79+
Example - Combining with Embedded Mode:
const config = {
experienceId: "your-experience-id",
apiUrl: "https://nexus-api.uat.knowbl.com/api/v2",
// Both can be used together
embedded: true,
useShadowDOM: true,
// All other configuration options work normally
window: {
header: { title: "Support Chat" },
},
bubbles: {
assistant: { backgroundColor: "#f3f4f6" },
},
};
NexusChatWidget.init(config, document.getElementById("chat-container"));
Read-Only Mode
The widget supports a read-only mode that disables user interactions while preserving the ability to view existing conversations. This is useful for maintenance windows, session reviews, demo previews, and rate limiting scenarios.
Initial Configuration
Enable read-only mode when initializing the widget:
const widget = NexusChatWidget.init({
experienceId: "your-experience-id",
apiUrl: "https://api.example.com",
advanced: {
// Enable read-only mode on initialization
readOnly: true,
// Optional: Configure the indicator badge
readOnlyIndicator: {
show: true,
text: "Preview Mode",
backgroundColor: "#FEF3C7",
textColor: "#92400E"
}
}
});Runtime API
Toggle read-only mode dynamically using the setReadOnly() method:
// Get widget instance
const widget = NexusChatWidget.init(config);
// Enable read-only mode
widget.setReadOnly(true);
// Disable read-only mode
widget.setReadOnly(false);
// Enable with custom indicator badge
widget.setReadOnly(true, {
indicator: {
show: true,
text: "Maintenance",
backgroundColor: "#FEF3C7",
textColor: "#92400E"
}
});
// Enable but allow copy button
widget.setReadOnly(true, {
allowCopy: true // Default: true
});
// Full options example
widget.setReadOnly(true, {
indicator: {
show: true,
text: "Rate Limited - Try again in 60s",
backgroundColor: "#FEE2E2",
textColor: "#991B1B"
},
allowCopy: false // Disable copy button too
});Method Signature:
setReadOnly(value: boolean, options?: SetReadOnlyOptions): voidOptions:
| Property | Type | Default | Description |
|---|---|---|---|
indicator.show | boolean | true | Show/hide the indicator badge |
indicator.text | string | "Unavailable" | Badge text |
indicator.backgroundColor | string | "#FEF3C7" | Badge background color |
indicator.textColor | string | "#92400E" | Badge text color |
allowCopy | boolean | true | Allow copy button when readonly |
What Gets Disabled
When read-only mode is enabled, the following interactions are disabled:
- Message input field (grayed out, not focusable)
- Send button
- Action buttons from responses (visible but disabled)
- Menu button (clear chat, etc.)
- Disclaimer accept button
- Feedback buttons (hidden)
Optionally kept enabled:
- Copy button (configurable via
allowCopy) - Scrolling through messages
- Widget open/close
- TTS playback (if configured)
Read-Only Change Events
Listen for read-only state changes:
widget.on('readOnlyChange', (data) => {
console.log('Read-only mode:', data.readOnly);
console.log('Indicator config:', data.indicator);
if (data.readOnly) {
// Update your UI to reflect read-only state
document.getElementById('chat-status').textContent = 'Chat is currently unavailable';
} else {
document.getElementById('chat-status').textContent = 'Chat is available';
}
});Event Data:
| Property | Type | Description |
|---|---|---|
timestamp | string | ISO timestamp of the change |
sessionId | string | null | Current session ID |
readOnly | boolean | Current read-only state |
indicator | object | null | Current indicator configuration |
Use Cases
Maintenance Mode:
// During planned maintenance
widget.setReadOnly(true, {
indicator: {
show: true,
text: "Scheduled Maintenance",
backgroundColor: "#DBEAFE",
textColor: "#1D4ED8"
}
});Session Review:
// Supervisors reviewing past conversations
widget.setReadOnly(true, {
indicator: {
show: true,
text: "Review Mode",
backgroundColor: "#E0E7FF",
textColor: "#4338CA"
},
allowCopy: true // Allow copying for documentation
});Rate Limiting:
// When rate limit is hit
let remainingSeconds = 60;
widget.setReadOnly(true, {
indicator: {
show: true,
text: `Rate limited - ${remainingSeconds}s`,
backgroundColor: "#FEE2E2",
textColor: "#991B1B"
}
});
// Update countdown
const interval = setInterval(() => {
remainingSeconds--;
if (remainingSeconds > 0) {
widget.setReadOnly(true, {
indicator: {
show: true,
text: `Rate limited - ${remainingSeconds}s`,
backgroundColor: "#FEE2E2",
textColor: "#991B1B"
}
});
} else {
clearInterval(interval);
widget.setReadOnly(false);
}
}, 1000);Demo/Preview Mode:
// Show widget preview before user authentication
widget.setReadOnly(true, {
indicator: {
show: true,
text: "Preview",
backgroundColor: "#F3E8FF",
textColor: "#7C3AED"
}
});
// Enable after user logs in
authService.onLogin(() => {
widget.setReadOnly(false);
});See the Read-Only Mode Demo for an interactive example.
Complete Example
Here’s a full configuration example:
window.NexusChatWidget.init({
// Required - replace with your actual experience ID
experienceId: "f2dc12a9-080e-47a3-b6d6-13e32bfaf83e ",
apiUrl: "https://nexus-api.uat.knowbl.com/api/v2",
// Theme
theme: "light",
// Button
button: {
position: "bottom-right",
text: "Chat with us",
backgroundColor: "#4CAF50",
textColor: "#ffffff",
size: "medium",
},
// Window
window: {
width: "400px",
height: "700px",
header: {
title: "Customer Support",
backgroundColor: "#4CAF50",
textColor: "#ffffff",
},
},
// Bubbles
bubbles: {
user: {
backgroundColor: "#4CAF50",
textColor: "#ffffff",
},
assistant: {
showAvatar: true,
avatarUrl: "/images/bot-avatar.png",
},
},
// Input
input: {
placeholder: "Ask us anything...",
sendButton: {
color: "#4CAF50",
},
},
// Messages
messages: {
welcomeMessage: "Hi! How can we help you today?",
},
// Features
features: {
voice: { enabled: true },
feedback: {
enabled: true,
style: "thumbs",
},
},
// Disclaimer
disclaimer: {
content:
"This chatbot provides automated responses. For urgent matters, please contact support.",
collapsible: true,
defaultExpanded: false,
},
});
Action Buttons
For detailed documentation on configuring action buttons, including ActionBehavior and PostClickBehavior properties, see the Actions Configuration page.
Next Steps
- Events System - Track widget interactions
- Message Interceptor - Handle messages programmatically
- Interactive Demos - See configurations in action
- Storage Management - Configure storage limits and manage chat history