1- import React , { useState , useEffect , useMemo , useCallback } from 'react' ;
1+ import React , { useState , useEffect , useMemo , useCallback , useRef } from 'react' ;
22import type { ChatWidgetProps , DeploymentConfig } from '../../types' ;
33import { useChat } from '../../hooks/useChat' ;
44import { createAPIClient } from '../../api/client' ;
@@ -7,7 +7,7 @@ import { ChatContainer } from '../ChatContainer';
77import { ChatToggleButton } from '../ChatToggleButton' ;
88import { ErrorState } from '../ErrorState' ;
99import styles from './ChatWidget.module.css' ;
10- import { DEFAULT_ENGINE_URL } from '../../api/client' ;
10+ import { DEFAULT_ENGINE_URL } from '../../api/client' ;
1111
1212type ErrorType = 'not_found' | 'network' | 'unknown' | null ;
1313
@@ -20,6 +20,7 @@ export const ChatWidget: React.FC<ChatWidgetProps> = ({
2020 defaultOpen = false ,
2121 primaryColor,
2222 agentName,
23+ agentLogoUrl,
2324 welcomeMessage,
2425 showBranding,
2526 className,
@@ -29,41 +30,38 @@ export const ChatWidget: React.FC<ChatWidgetProps> = ({
2930 onError,
3031} ) => {
3132 const [ isOpen , setIsOpen ] = useState ( defaultOpen ) ;
32- const [ config , setConfig ] = useState < DeploymentConfig | null > ( null ) ;
33+ const [ baseConfig , setBaseConfig ] = useState < DeploymentConfig | null > ( null ) ;
3334 const [ isLoadingConfig , setIsLoadingConfig ] = useState ( true ) ;
3435 const [ errorType , setErrorType ] = useState < ErrorType > ( null ) ;
3536 const [ errorMessage , setErrorMessage ] = useState < string | undefined > ( ) ;
37+
38+ // Track if initial config has been loaded to prevent re-renders
39+ const hasLoadedConfig = useRef ( false ) ;
3640
37- // Create API client based on mode
41+ // Create API client based on mode - NOT dependent on styling props
3842 const apiClient = useMemo ( ( ) => {
3943 if ( mockMode ) {
40- return createMockAPIClient (
41- { primaryColor, agentName, welcomeMessage } ,
42- mockResponses
43- ) ;
44+ // Pass empty config to mock client - we'll apply overrides via effectiveConfig
45+ return createMockAPIClient ( { } , mockResponses ) ;
4446 }
4547 return createAPIClient ( apiBaseUrl ) ;
46- } , [ mockMode , primaryColor , agentName , welcomeMessage , mockResponses , apiBaseUrl ] ) ;
48+ } , [ mockMode , mockResponses , apiBaseUrl ] ) ;
4749
48- // Fetch deployment config
50+ // Fetch deployment config - only when embedId or mockMode changes
4951 const fetchConfig = useCallback ( async ( ) => {
52+ // Skip if already loaded and not a forced refresh
53+ if ( hasLoadedConfig . current && baseConfig ) {
54+ return ;
55+ }
56+
5057 setIsLoadingConfig ( true ) ;
5158 setErrorType ( null ) ;
5259 setErrorMessage ( undefined ) ;
5360
5461 try {
5562 const deploymentConfig = await apiClient . getDeploymentConfig ( embedId ) ;
56- setConfig ( {
57- ...deploymentConfig ,
58- // Allow prop overrides
59- primaryColor : primaryColor ?? deploymentConfig . primaryColor ,
60- agentName : agentName ?? deploymentConfig . agentName ,
61- welcomeMessage : welcomeMessage ?? deploymentConfig . welcomeMessage ,
62- styling : {
63- ...( deploymentConfig . styling ?? { } ) ,
64- ...( showBranding !== undefined ? { showBranding } : { } ) ,
65- } ,
66- } ) ;
63+ setBaseConfig ( deploymentConfig ) ;
64+ hasLoadedConfig . current = true ;
6765 } catch ( err ) {
6866 const error = err instanceof Error ? err : new Error ( 'Failed to load config' ) ;
6967
@@ -79,50 +77,56 @@ export const ChatWidget: React.FC<ChatWidgetProps> = ({
7977
8078 // In mock mode, use default config on error
8179 if ( mockMode ) {
82- setConfig ( {
80+ setBaseConfig ( {
8381 embedId,
8482 deploymentId : 'mock-deployment' ,
8583 workerId : 'mock-worker' ,
8684 flowId : 'mock-flow' ,
87- primaryColor : primaryColor ?? '#1a1a2e' ,
88- agentName : agentName ?? 'AI Assistant' ,
89- styling : showBranding !== undefined ? { showBranding } : undefined ,
85+ primaryColor : '#1a1a2e' ,
86+ agentName : 'AI Assistant' ,
87+ styling : { } ,
9088 } ) ;
9189 setErrorType ( null ) ;
90+ hasLoadedConfig . current = true ;
9291 }
9392
9493 onError ?.( error ) ;
9594 } finally {
9695 setIsLoadingConfig ( false ) ;
9796 }
98- } , [ embedId , mockMode , apiClient , primaryColor , agentName , welcomeMessage , onError ] ) ;
97+ } , [ embedId , mockMode , apiClient , onError , baseConfig ] ) ;
9998
99+ // Only fetch on mount or when embedId changes
100100 useEffect ( ( ) => {
101+ hasLoadedConfig . current = false ;
101102 fetchConfig ( ) ;
102- } , [ fetchConfig ] ) ;
103+ } , [ embedId , mockMode , apiClient ] ) ;
103104
104- // Create a stable config for useChat
105- const chatConfig = useMemo ( ( ) : DeploymentConfig => {
106- if ( config ) return config ;
107- return {
105+ // Effective config: base config + prop overrides (updates instantly without re-fetch)
106+ const effectiveConfig = useMemo ( ( ) : DeploymentConfig => {
107+ const base = baseConfig ?? {
108108 embedId,
109109 deploymentId : '' ,
110110 workerId : '' ,
111111 flowId : '' ,
112- primaryColor,
113- agentName,
114- welcomeMessage,
115- styling :
116- showBranding !== undefined
117- ? {
118- showBranding,
119- }
120- : undefined ,
121112 } ;
122- } , [ config , embedId , primaryColor , agentName , welcomeMessage , showBranding ] ) ;
113+
114+ return {
115+ ...base ,
116+ // Apply prop overrides - these update instantly without triggering re-fetch
117+ primaryColor : primaryColor ?? base . primaryColor ?? '#1a1a2e' ,
118+ agentName : agentName ?? base . agentName ,
119+ agentLogoUrl : agentLogoUrl ?? base . agentLogoUrl ,
120+ welcomeMessage : welcomeMessage ?? base . welcomeMessage ,
121+ styling : {
122+ ...( base . styling ?? { } ) ,
123+ ...( showBranding !== undefined ? { showBranding } : { } ) ,
124+ } ,
125+ } ;
126+ } , [ baseConfig , embedId , primaryColor , agentName , agentLogoUrl , welcomeMessage , showBranding ] ) ;
123127
124128 const chat = useChat ( {
125- config : chatConfig ,
129+ config : effectiveConfig ,
126130 apiClient,
127131 mockMode,
128132 onSessionStart,
@@ -131,13 +135,13 @@ export const ChatWidget: React.FC<ChatWidgetProps> = ({
131135 onError,
132136 } ) ;
133137
134- // CSS custom properties for theming
138+ // CSS custom properties for theming - uses effectiveConfig for instant updates
135139 const themeStyle = useMemo (
136140 ( ) =>
137141 ( {
138- '--bb-primary-color' : config ?. primaryColor ?? primaryColor ?? '#1a1a2e' ,
142+ '--bb-primary-color' : effectiveConfig . primaryColor ?? '#1a1a2e' ,
139143 } ) as React . CSSProperties ,
140- [ config ?. primaryColor , primaryColor ]
144+ [ effectiveConfig . primaryColor ]
141145 ) ;
142146
143147 // Handle new chat
@@ -202,7 +206,7 @@ export const ChatWidget: React.FC<ChatWidgetProps> = ({
202206 >
203207 { isOpen || isInline ? (
204208 < ChatContainer
205- config = { chatConfig }
209+ config = { effectiveConfig }
206210 messages = { chat . messages }
207211 // toolCalls={chat.toolCalls} // Disabled tool running UI
208212 isLoading = { chat . isLoading }
@@ -213,8 +217,8 @@ export const ChatWidget: React.FC<ChatWidgetProps> = ({
213217 ) : (
214218 < ChatToggleButton
215219 onClick = { ( ) => setIsOpen ( true ) }
216- agentName = { config ? .agentName }
217- agentLogoUrl = { config ? .agentLogoUrl }
220+ agentName = { effectiveConfig . agentName }
221+ agentLogoUrl = { effectiveConfig . agentLogoUrl }
218222 />
219223 ) }
220224 </ div >
0 commit comments