React Integration with Periscope Web SDK
Integrating the Periscope Web SDK into a React application involves loading the SDK, creating a dedicated component for the chatbot, and then using that component within your application.
1. Load the SDK in public/index.html
Make the SDK globally available by adding the script tag to your public/index.html file. Place it preferably at the end of the <body> tag:
<!-- public/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script
async
src="https://media.periscopechat.com/sdk/chatbot-sdk.umd.js"
></script>
</body>
</html>
2. Create a Dedicated React Component
Encapsulating the chatbot in its own component is a good practice for modularity and reusability.
a. Create the component file (e.g., src/Chatbot.js or src/Chatbot.tsx):
// src/Chatbot.js
import React, { useEffect, useRef } from "react";
// Declare ChatbotSDK to inform TypeScript/JavaScript it's a global variable
// This is crucial because the SDK is loaded via a script tag, not as an ES module.
/* global ChatbotSDK */
const Chatbot = (props) => {
const {
socketUrl,
title = "Chat Support",
headerBgColor = "#007bff", // Default blue
containerId = "periscope-chatbot-container", // Unique ID for the chatbot instance
iconUrl,
initMessage,
questionPills,
buttonText,
buttonBgColor,
// iconComponent, // For advanced use with ReactNode
} = props;
const chatbotContainerRef = useRef(null);
const isSdkInitialized = useRef(false);
const sdkLoadAttempts = useRef(0);
const maxSdkLoadAttempts = 10; // e.g., 5 seconds if polling every 500ms
const sdkLoadInterval = useRef(null);
useEffect(() => {
const attemptSdkInitialization = () => {
if (typeof ChatbotSDK !== "undefined" && ChatbotSDK.init) {
initializeChatbot();
if (sdkLoadInterval.current) {
clearInterval(sdkLoadInterval.current);
sdkLoadInterval.current = null;
}
} else {
sdkLoadAttempts.current++;
if (sdkLoadAttempts.current <= maxSdkLoadAttempts) {
if (!sdkLoadInterval.current) {
// Start interval only if not already running
sdkLoadInterval.current = setInterval(() => {
attemptSdkInitialization();
}, 500);
}
} else {
console.error(
"ChatbotSDK could not be loaded after multiple attempts."
);
if (sdkLoadInterval.current) {
clearInterval(sdkLoadInterval.current);
sdkLoadInterval.current = null;
}
}
}
};
const initializeChatbot = () => {
if (!socketUrl) {
console.error("ChatbotSDK: socketUrl is required for initialization.");
return;
}
if (!containerId) {
console.error(
"ChatbotSDK: containerId is required for initialization."
);
return;
}
ChatbotSDK.init({
socketUrl,
title,
headerBgColor,
containerId,
iconUrl,
initMessage,
questionPills,
buttonText,
buttonBgColor,
// iconComponent, // Uncomment if using
});
isSdkInitialized.current = true;
};
attemptSdkInitialization();
// Cleanup interval on component unmount
return () => {
if (sdkLoadInterval.current) {
clearInterval(sdkLoadInterval.current);
}
// Optionally, you might want to call ChatbotSDK.destroy() or similar if the SDK provides it
};
}, [
socketUrl,
title,
headerBgColor,
containerId,
iconUrl,
initMessage,
questionPills,
buttonText,
buttonBgColor,
]); // Re-run effect if these props change
// --- Wrapper methods for SDK functionalities (optional) ---
// These can be exposed via ref or passed down as props if needed from parent
const identifyUser = (name, customer_uuid) => {
if (
isSdkInitialized.current &&
typeof ChatbotSDK !== "undefined" &&
ChatbotSDK.identify
) {
ChatbotSDK.identify(name, customer_uuid);
} else {
console.warn(
"ChatbotSDK not ready for identifyUser call or identify method unavailable."
);
}
};
const addCustomMetadata = (metadata) => {
if (
isSdkInitialized.current &&
typeof ChatbotSDK !== "undefined" &&
ChatbotSDK.addMetadata
) {
ChatbotSDK.addMetadata(metadata);
} else {
console.warn(
"ChatbotSDK not ready for addCustomMetadata call or addMetadata method unavailable."
);
}
};
const showChat = () => {
if (
isSdkInitialized.current &&
typeof ChatbotSDK !== "undefined" &&
ChatbotSDK.show
) {
ChatbotSDK.show();
} else {
console.warn(
"ChatbotSDK not ready for showChat call or show method unavailable."
);
}
};
const hideChat = () => {
if (
isSdkInitialized.current &&
typeof ChatbotSDK !== "undefined" &&
ChatbotSDK.hide
) {
ChatbotSDK.hide();
} else {
console.warn(
"ChatbotSDK not ready for hideChat call or hide method unavailable."
);
}
};
// Expose methods via ref if needed (example)
// useImperativeHandle(ref, () => ({
// identifyUser,
// addCustomMetadata,
// showChat,
// hideChat,
// }));
return (
<div id={containerId} ref={chatbotContainerRef}>
<chatbotSDK></chatbotSDK>
</div>
);
};
export default Chatbot;
3. Use the Chatbot Component in Your Application
You can now import and use your Chatbot component in any other component where you want the chatbot to appear. Pass the necessary configurations as props.
// Example: In your App.js or any other component
import React from "react";
import Chatbot from "./Chatbot"; // Ensure path is correct
import "./App.css";
function App() {
const tenantName = "<your_tenant_name>"; // Replace with your actual tenant name
return (
<div className="App">
<header className="App-header">
<h1>Welcome to My React App</h1>
</header>
<Chatbot
socketUrl={`https://\${tenantName}.periscopechat.com/web`}
title="Support Assistant"
headerBgColor="darkblue"
containerId="react-chatbot"
initMessage="Hello! How can I help you today with your React app?"
questionPills={["How to install?", "What are the features?"]}
buttonText="Chat with AI"
/>
{/* You can add buttons or other UI elements to interact with the chatbot programmatically */}
{/* For example, using a ref to call showChat() or hideChat() */}
</div>
);
}
export default App;
4. Styling (Optional)
You might want to add some basic styling for the container or the page.
/* src/App.css or your main stylesheet */
.App {
text-align: center;
}
.App-header {
background-color: #282c34;
padding: 20px;
color: white;
margin-bottom: 20px;
}
/* Ensure the chatbot container doesn't cause layout shifts if it's initially empty */
#react-chatbot chatbotSDK {
/* The SDK will manage its own internal styling and visibility */
}
Key Considerations:
- Global SDK: The
ChatbotSDKis loaded globally. The/* global ChatbotSDK */comment helps linters understand thatChatbotSDKis a global variable. - SDK Loading: The
useEffecthook with theattemptSdkInitializationlogic ensures thatChatbotSDK.initis called only after the SDK script has loaded. It polls a few times before giving up. - Props: The
Chatbotcomponent accepts props to configure the SDK, making it reusable. - Container ID: Ensure the
containerIdpassed to theChatbotcomponent matches theidof thedivin its render method. This is where the SDK will mount the chatbot UI. chatbotSDKelement: The<chatbotSDK></chatbotSDK>custom element is part of how the Periscope SDK injects its UI. Ensure this is present within the containerdiv.- Tenant Name: Remember to replace
<your_tenant_name>insocketUrlwith your actual Periscope tenant name. - Interacting Programmatically: To call methods like
identify,show,hide, oraddMetadatafrom a parent component:- You can pass callback props to the
Chatbotcomponent that then call the SDK methods. - Alternatively, use
React.forwardRefanduseImperativeHandlein theChatbotcomponent to expose these methods directly to a parent component via aref. The example code includes a commented-out section foruseImperativeHandle.
- You can pass callback props to the
This setup provides a robust way to integrate the Periscope Web SDK into your React applications.