# Embed Chat Into A ReactJS SPA

**URL:** <https://community.hubspot.com/t/embed-chat-into-a-reactjs-spa/14838>\
**Category:** CMS Development\
**Tags:** design-manager\
**Created:** [June 18, 2019, 1:40pm UTC](https://community.hubspot.com/t/embed-chat-into-a-reactjs-spa/14838 "2019-06-18T13:40:30Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![amjadg](https://d10pqhm3lwygmk.cloudfront.net/user_avatar/community.hubspot.com/amjadg/32/34870_2.png) [@amjadg](https://community.hubspot.com/u/amjadg)\
**Post date:** [June 18, 2019, 1:40pm UTC](https://community.hubspot.com/t/embed-chat-into-a-reactjs-spa/14838/1 "2019-06-18T13:40:30Z")

</div>

So, is there an embed pattern to embed/integrate the chatbot into my reactJS SPA ? I couldn’y find an example or even an npm module for the chatbot. Is there a clean integration pattern for this into react ? Please share details.

---

<div class="post-metadata">

**Author:** ![lscanlan](https://d10pqhm3lwygmk.cloudfront.net/user_avatar/community.hubspot.com/lscanlan/32/66262_2.png) [@lscanlan](https://community.hubspot.com/u/lscanlan)\
**Post date:** [June 25, 2019, 9:34pm UTC](https://community.hubspot.com/t/embed-chat-into-a-reactjs-spa/14838/2 "2019-06-25T21:34:33Z")

</div>

Hi @amjadg,

I haven’t done this myself but I’ve been doing some searching around our forums. Here’s an example from a while back where someone added the tracking code to a React app: [https://community.hubspot.com/t5/APIs-Integrations/Tracking-code-for-a-single-page-application-made-in-React/m-p/226513](https://community.hubspot.com/t5/APIs-Integrations/Tracking-code-for-a-single-page-application-made-in-React/m-p/226513). If you look at the bottom response there, the user built a page listener component. Maybe that would help with what you’re trying to do?

I also found this: [https://community.hubspot.com/t5/CMS-Development/Chat-integration-issue-with-React/m-p/275219](https://community.hubspot.com/t5/CMS-Development/Chat-integration-issue-with-React/m-p/275219) while doing a search, but I see you’ve already tried posting to that thread. Maybe we can get @Rajesh16 's attention here, and see if they’ve made any progress.

Also, maybe bumping this thread back to the top will get some more eyes on it.

---

<div class="post-metadata">

**Author:** ![PaulRBerg](https://d10pqhm3lwygmk.cloudfront.net/user_avatar/community.hubspot.com/paulrberg/32/97207_2.png) [@PaulRBerg](https://community.hubspot.com/u/PaulRBerg)\
**Post date:** [December 9, 2019, 11:20am UTC](https://community.hubspot.com/t/embed-chat-into-a-reactjs-spa/14838/3 "2019-12-09T11:20:44Z")

</div>

Any updates on this?  
I tried these methods:  
1. Add the script in “index.html”  
2. Append the script to the “body” element in a “useEffect” Hook defined in the top-level “App” component  
None worked (with React 16.11.0). Unfortunately, this is a showstopper, forcing me to go find another live chat product.

```auto
<body>
 <noscript>You need to enable JavaScript to run this app.</noscript>
 <div id="root"></div>
 <script src="https://js.hs-scripts.com/MY_ID.js" async></script>
</body>

```

```auto
React.useEffect(() => {
 (function loadHubSpot() {
 const d = document;
 const s = d.createElement("script");

 s.src="https://js.hs-scripts.com/MY_ID.js";
 s.async = true;
 d.getElementsByTagName("body")[0].appendChild(s);
 })();
}, [])

```

---

<div class="post-metadata">

**Author:** ![alexlisong](https://d10pqhm3lwygmk.cloudfront.net/user_avatar/community.hubspot.com/alexlisong/32/104913_2.png) [@alexlisong](https://community.hubspot.com/u/alexlisong)\
**Post date:** [January 12, 2020, 12:46am UTC](https://community.hubspot.com/t/embed-chat-into-a-reactjs-spa/14838/4 "2020-01-12T00:46:05Z")

</div>

Have same issue here, any updates?

---

<div class="post-metadata">

**Author:** ![anitaernszt](https://d10pqhm3lwygmk.cloudfront.net/user_avatar/community.hubspot.com/anitaernszt/32/84335_2.png) [@anitaernszt](https://community.hubspot.com/u/anitaernszt)\
**Post date:** [March 25, 2020, 9:22pm UTC](https://community.hubspot.com/t/embed-chat-into-a-reactjs-spa/14838/5 "2020-03-25T21:22:48Z")

</div>

Try adding \<script … /\> to the \<head\>\</head\> component instead of \<body\>\</body\>

I’ve added to index.html this way and it’s working for me

---

<div class="post-metadata">

**Author:** ![padecaluwe](https://d10pqhm3lwygmk.cloudfront.net/user_avatar/community.hubspot.com/padecaluwe/32/102945_2.png) [@padecaluwe](https://community.hubspot.com/u/padecaluwe)\
**Post date:** [July 14, 2020, 8:31am UTC](https://community.hubspot.com/t/embed-chat-into-a-reactjs-spa/14838/6 "2020-07-14T08:31:58Z")

</div>

we have the same problem here, nothing worked

---

<div class="post-metadata">

**Author:** ![slowtokyo](https://d10pqhm3lwygmk.cloudfront.net/user_avatar/community.hubspot.com/slowtokyo/32/5125_2.png) [@slowtokyo](https://community.hubspot.com/u/slowtokyo)\
**Post date:** [July 28, 2020, 6:59am UTC](https://community.hubspot.com/t/embed-chat-into-a-reactjs-spa/14838/7 "2020-07-28T06:59:18Z")

</div>

This worked for me as well.

---

<div class="post-metadata">

**Author:** ![kierana](https://d10pqhm3lwygmk.cloudfront.net/user_avatar/community.hubspot.com/kierana/32/44503_2.png) [@kierana](https://community.hubspot.com/u/kierana)\
**Post date:** [July 16, 2020, 12:56pm UTC](https://community.hubspot.com/t/embed-chat-into-a-reactjs-spa/14838/8 "2020-07-16T12:56:26Z")

</div>

Something like this should work:

```auto
// Create a hook to seperate out logic.const useHubspotChat = (portalId) => {
 const [hasLoaded, setHasLoaded] = React.useState(false);
 const [activeConversation, setActiveConversation] = React.useState(false);
 const eventRef = React.useRef(null);

 React.useEffect(() => {
 console.log('hey')

 // Add event listener.
 window.hsConversationsOnReady = [() => {
 setHasLoaded(true);
 }];

 // Create script component.
 let script = document.createElement('script');
 script.src=`//js.hs-scripts.com/${portalId}.js`;
 script.async = true;

 document.body.appendChild(script);

 return () => {
 document.body.removeChild(script);
 window.hsConversationsOnReady = [];
 }

 }, []);

 // Subscripe to conversation events.
 React.useEffect(() => {
 eventRef.current = (payload) => {
 setActiveConversation(payload.conversation.conversationId);
 }

 if (hasLoaded) {
 window.HubSpotConversations.on( 'conversationStarted', eventRef.current);
 }

 return () => {
 window.HubSpotConversations.off('conversationStarted', eventRef.current);
 }

 }, [hasLoaded]);

 const openHandler = React.useCallback(() => {
 if (hasLoaded) {
 window.HubSpotConversations.widget.open();
 }
 }, [hasLoaded]);

 const closeHandler = React.useCallback(() => {
 if (hasLoaded) {
 window.HubSpotConversations.widget.close();
 }
 }, [hasLoaded])

 return {
 hasLoaded, 
 activeConversation,
 openHandler,
 closeHandler
 };
}

const App = () => {
 const { hasLoaded, activeConversation, openHandler, closeHandler } = useHubspotChat(<portalidhere>);
 return (
 <div>
 hi {hasLoaded ? 'yes': 'no'} 
 conversation: { activeConversation } 
 <button onClick={openHandler}>click me to open</button>
 <button onClick={closeHandler}>click me to close</button>
 </div>)
}

const rootElement = document.getElementById("root");

ReactDOM.render(<App />, rootElement);

```

---

<div class="post-metadata">

**Author:** ![RScotten](https://d10pqhm3lwygmk.cloudfront.net/user_avatar/community.hubspot.com/rscotten/32/195799_2.png) [@RScotten](https://community.hubspot.com/u/RScotten)\
**Post date:** [July 18, 2022, 1:30pm UTC](https://community.hubspot.com/t/embed-chat-into-a-reactjs-spa/14838/9 "2022-07-18T13:30:59Z")

</div>

Learning about this was super helpful:

`window.HubSpotConversations.widget...`

---

<div class="post-metadata">

**Author:** ![Mark\_Ryba](https://d10pqhm3lwygmk.cloudfront.net/user_avatar/community.hubspot.com/mark_ryba/32/22744_2.png) [@Mark\_Ryba](https://community.hubspot.com/u/Mark_Ryba)\
**Post date:** [July 16, 2020, 1:25pm UTC](https://community.hubspot.com/t/embed-chat-into-a-reactjs-spa/14838/10 "2020-07-16T13:25:40Z")

</div>

It should work, but it may depend on how you’re handling routing. Basically the way the chat function works is you add the standard HS scripts and then configure (in HubSpot) where and when the live chat should pop up. Basically the script needs to be available for the whole app, and then the triggers are handled on HubSpot’s side instead of in your codebase. I’m not aware of an NPM or webpack-friendly method of bundling that script, however. I’ve always done it manually.

There _may_ be a conflict between React versions since HubSpot uses React for a lot of their on-page tools. That shouldn’t happen, but it is something I’ve run into in the past. React Dev Tools might help diagnose if there’s something like that going on.

---

<div class="post-metadata">

**Author:** ![kierana](https://d10pqhm3lwygmk.cloudfront.net/user_avatar/community.hubspot.com/kierana/32/44503_2.png) [@kierana](https://community.hubspot.com/u/kierana)\
**Post date:** [July 16, 2020, 1:42pm UTC](https://community.hubspot.com/t/embed-chat-into-a-reactjs-spa/14838/11 "2020-07-16T13:42:56Z")

</div>

I guess if you’re using React Router - or some other router you can simply invoke window.HubSpotConversations.widget.refresh(); which apparently re-runs the page matching logic based on the current URL.

---

<div class="post-metadata">

**Author:** ![DA3](https://d10pqhm3lwygmk.cloudfront.net/user_avatar/community.hubspot.com/da3/32/77060_2.png) [@DA3](https://community.hubspot.com/u/DA3)\
**Post date:** [August 31, 2020, 1:57pm UTC](https://community.hubspot.com/t/embed-chat-into-a-reactjs-spa/14838/12 "2020-08-31T13:57:53Z")

</div>

```lia-code-sample
class App extends Component {

 componentDidMount() {

 const script = document.createElement("script");

 script.src="//js.hs-scripts.com/{}.js";
 script.async = true;

 document.body.appendChild(script);

 }

 render() {

 return (
 <>
 <AppLayout/>
 <Cookiebar/>
 </>
 );
 }
}

```

---

<div class="post-metadata">

**Author:** ![JIanniciello](https://d10pqhm3lwygmk.cloudfront.net/user_avatar/community.hubspot.com/jianniciello/32/48852_2.png) [@JIanniciello](https://community.hubspot.com/u/JIanniciello)\
**Post date:** [August 30, 2021, 2:40pm UTC](https://community.hubspot.com/t/embed-chat-into-a-reactjs-spa/14838/13 "2021-08-30T14:40:44Z")

</div>

Jquery question here:  
Does anyone know if it is possible to embed a chatbot into a web app written in Jquery? Thanks!

---

<div class="post-metadata">

**Author:** ![DLemesh](https://avatars.discourse-cdn.com/v4/letter/d/b5a626/32.png) [@DLemesh](https://community.hubspot.com/u/DLemesh)\
**Post date:** [September 7, 2022, 8:44am UTC](https://community.hubspot.com/t/embed-chat-into-a-reactjs-spa/14838/14 "2022-09-07T08:44:39Z")

</div>

Just wanted to post my take on it.  
I needed the ability to control the widget via a custom button in my app.

I created a provider for the widget and used CSS to show/hide the widget.

First and most important, is to make sure the widget won’t load initally, so in the **index.html** you sould add this snippet at the end of the **\<body\>** tag:

`<!-- Start of HubSpot Embed Code --><script> window.hsConversationsSettings = { loadImmediately: false, inlineEmbedSelector: '#hubspot-conversations-inline-embed-selector', };</script><script type="text/javascript" id="hs-script-loader" async defer src="//js-na1.hs-scripts.com/your-portal-id-here.js"></script><!-- End of HubSpot Embed Code -->`

This is the provider: **HubspotConversationsProvider.tsx**

`import { createStyles } from '@mantine/core';import React, { createContext, FC, useCallback, useContext, useEffect, useState,} from 'react';import ReactDOM from 'react-dom';import { useLocation } from 'react-router-dom';interface HubspotConversationsContextType { toggleWidget: () => void; isOpen: boolean; unreadMessagesCount: number;}const HubspotConversationsContext = createContext<HubspotConversationsContextType | null>(null);const HUBSPOT_INLINE_EMBED_ELEMENT_ID = 'hubspot-conversations-inline-embed-selector';export const HubspotConversationsProvider: FC = ({ children }) => { const location = useLocation(); const [isReady, setIsReady] = useState(false); const [isOpen, setIsOpen] = useState(false); const [unreadMessagesCount, setUnreadMessagesCount] = useState(0); const { classes } = useStyles(isOpen); const user = useCurrentUser(); // This is for Visitor Identification API const hubspotToken = useHubspotConversationsToken(); const hideWidget = useCallback(() => { setIsOpen(false); }, []); const showWidget = useCallback(() => { if (!isReady) return; window.HubSpotConversations.widget.load(); window.HubSpotConversations.widget.open(); setIsOpen(true); }, [isReady]); const toggleWidget = useCallback(() => { if (isOpen) { hideWidget(); } else { showWidget(); } }, [hideWidget, isOpen, showWidget]); const onConversationsReady = useCallback(() => { setIsReady(true); }, []); useEffect( function init() { if (window.HubSpotConversations) { onConversationsReady(); } else { window.hsConversationsOnReady = [onConversationsReady]; } }, [onConversationsReady] ); useEffect( function updateSettingsAndLoadWidget() { if (hubspotToken.isFetched && hubspotToken?.data) { window.hsConversationsSettings = { ...window.hsConversationsSettings, identificationEmail: user?.email, identificationToken: hubspotToken.data.token, }; window.HubSpotConversations.widget.load(); } }, [hubspotToken.data, hubspotToken.isFetched, user?.email] ); useEffect( function addEventListeners() { if (!isReady) return; function listener(payload: { unreadCount: number }) { setUnreadMessagesCount(payload.unreadCount); } window.HubSpotConversations.on( 'unreadConversationCountChanged', listener ); return () => { window.HubSpotConversations.off( 'unreadConversationCountChanged', listener ); }; }, [isReady] ); useEffect( function refreshConversationsOnRouteChange() { if (!isReady) return; window.HubSpotConversations.widget.refresh(); }, [isReady] ); return ( <HubspotConversationsContext.Provider value={{ isOpen, toggleWidget, unreadMessagesCount }} > {children} {ReactDOM.createPortal( <div className={classes.chatWidgetContainer} id={HUBSPOT_INLINE_EMBED_ELEMENT_ID} />, document.body )} </HubspotConversationsContext.Provider> );};const useStyles = createStyles((theme, isOpen: boolean) => ({ chatWidgetContainer: { overflow: 'hidden', zIndex: 2147483647, // this is the max possible value position: 'fixed', top: 75, right: theme.spacing.xl, height: 500, width: 376, display: isOpen ? 'block' : 'none', borderRadius: theme.radius.md, backgroundColor: theme.white, boxShadow: '0 5px 20px rgb(0 0 0 / 10%)', '#hubspot-conversations-inline-parent': { width: '100%', height: '100%', }, '#hubspot-conversations-inline-iframe': { width: '100%', height: '100%', border: 'none', }, },}));export function useHubspotConversations() { const context = useContext(HubspotConversationsContext); if (context === null) { throw new Error( 'useHubspotConversations must be used within HubspotConversationsProvider' ); } return context;}declare global { interface Window { hsConversationsSettings: Record<string, any>; hsConversationsOnReady: Array<() => void>; HubSpotConversations: { on: any; off: any; widget: { status: () => { loaded: boolean; pending: boolean }; load: (params?: { widgetOpen: boolean }) => void; remove: () => void; open: () => void; close: () => void; refresh: (openToNewThread?: boolean) => void; }; }; }}`

And the usage can be something like that:

`function App() { return ( <HubspotConversationsProvider> <ChatWidgetButton /> </HubspotConversationsProvider> );}function ChatWidgetButton() { const { toggleWidget } = useHubspotConversations(); return <button onClick={toggleWidget}>Toggle chat</button>;}`
