Chatbots are a pretty common occurrence in websites. I haven’t worked on a lot of them before, but I’ve interacted with quite a few for lodging complaints, troubleshooting, etc. The recent boom in AI space (thanks, ChatGPT) has made sure I’ll be interacting with a lot more of them.
So, I’ll start by giving a brief overview of Tarka Chat — a framework-independent library to ease the chatbot UI development part.
Disclaimer: I work at Tarka Labs which builds and maintains Tarka Chat.
Tarka Chat
Tarka Chat is an open source library that sets up a neat chatbot UI and can be configured to work with chat APIs. According to its Github description:
Tarka Chat UI is a plug and play javascript library to integrate a chat assistant to your website in one line
Adding Tarka Chat to a website is pretty easy — a script tag with source pointing to the tarka chat CDN file. The script tag will look something like this:
<script src="https://d1fmfone96g0x2.cloudfront.net/tarka-chat-2.4.4.umd.js"></script>
The latest version when writing this blog was 2.4.4.
You can find the current version in the release section in its Github repo, and update the import if required.
Once imported, Tarka Chat can be initialised with multiple properties.
TarkaChat.init({
title: "Personal Assistant",
botName: "superbot",
greeting: "Welcome. How can I assist you today?",
themeColor: "#F0DAFB",
selectorId: "chatbot",
expand: true,
enableUpload: true,
preChatRenderer: function (onClose) {
// Return a DOM Node that can be attached to the chatbot UI
// Use the onClose callback to close the pre-chat screen
const button = document.createElement("button");
button.innerText = "Close pre-chat";
button.addEventListener("click", onClose);
return button;
},
submitHandler: async function (message) {
// Do API calls
// after getting response return the response string
return Promise.resolve("Recieved response");
},
});
preChatRenderer is an optional property, and it accepts a method which takes an pre-chat close method as param and returns a DOM node containing the preChat UI.
submitHandler is a required property, and it accepts an async method that takes a user message string as param and returns another bot reply string.
Integrating Tarka Chat with React/Vue apps
In most cases, using Tarka Chat in React or other JS frameworks is pretty simple — you can simply import the script in the root HTML file and initialize from inside the JS app. With this approach, you can store and use the chat reference and its associated methods with your app. You can also use the preChatRenderer to show a welcome screen or something similar.
The shortcoming with this approach comes to light when you want to add reactivity inside the chat UI. For example, let’s consider the following scenario:
- Fetch a list from an API asynchronously, while showing a loading message in the pre-chat screen
- After fetching the list, render the list items as buttons in the pre-chat screen
- Clicking a list item button closes the pre-chat screen and moves to the chat screen
- Each time the user sends a message, the list item that the user selected with the button is also sent to the server
How exactly do we implement this in Tarka chat? For simple stuff like this, we could go with direct DOM manipulation, keeping it separated from the JS framework. But what if we want another screen with sub lists, and then another screen? When the complexity grows, we can initialise the JS framework inside Tarka Chat. We’ll go through both the approaches below.
Direct DOM Manipulation:
This approach is pretty self-explanatory. The pre-chat render method which is passed to Tarka Chat would look something like this:
function renderPreChat(closePreChat) {
const container = document.createElement("div");
container.id = "my-prechat";
container.innerHTML = "Loading...";
const addItemToDOM = item => {
const button = document.createElement("button");
button.innerText = item;
button.onclick = () => {
localStorage.setItem("selected-list-item", item);
closePreChat();
};
container.appendChild(button);
}
loadListFromApi().
then(list => {
container.innerHTML = "";
list.forEach(addItemToDOM);
}).
catch(err => {
console.error(err);
container.innerHTML = "Failed to load data";
});
return container;
}
And my message submit handler looked something like this:
function onMessageSubmit(message) {
const selectedListItem = localStoage.getItem("selected-list-item");
return sendMessage(message, selectedListItem);
}
Initialising React inside Tarka Chat
A really simple approach that can solve this issue is to initialise React inside the pre-chat screen. In simpler terms, the DOM node returned by the function associated to preChatRenderer property can be initialised as a React root (kind of like a new React app). This is the container that is later returned by the renderPreChat
method.
function renderPreChat(closePreChat) {
const container = document.createElement("div");
createRoot(container).render(<PreChat onClose={closePreChat} />);
return container;
}
Here, React is given control of the DOM container for pre-chat. So, the PreChat
component can contain a full blown React application inside it.
Same, but in Vue
For adding Tarka Chat in Vue, the approach is similar — creating a DOM element as container and handing it over to Vue. Vue has changed a lot between Vue 2 and Vue 3, including how app initialization is done. So, we’ll go over both implementations here.
Vue 2 — constructor
Vue 2 replaces the root node with the component template. This can mess up with class attributes that Tarka Chat sets on the returned container. So, we can do this:
function renderPreChat(closePreChat) {
const container = document.createElement("div");
const vueRoot = document.createElement("div");
container.appendChild(vueRoot);
new Vue({
el: vueRoot,
render: (createElement) =>
createElement(PreChat, { props: { onClose: closePreChat } })
});
return container;
}
Here, you can notice the extra child div vueRoot
which acts like an expendable for Vue initialisation.
Vue 3 — createApp method
Vue 3 replaced constructor approach with a createApp method for Vue app initialisation which looks like this:
function renderPreChat(closePreChat) {
const container = document.createElement("div");
createApp(PreChat).mount(container);
return container;
}
Unlike Vue 2, we don’t have to add a separate child DOM node for initialising the app. This is because Vue 3 doesn’t replace the DOM node which is used for initialising Vue app, but rather initialises the Vue app as a child of the DOM node.
Conclusion
These are some of the simplest, direct approaches I can think of. There might be other approaches using Refs and stuff. Let me know in the comments if you think another approach would have been better.
Also a Disclaimer — At the time of writing this blog, the latest version of Tarka Chat was 2.4.4
as per its Github releases. There might be a few changes if the version has changed. Make sure to take a look at its README and make appropriate changes.
Additionally, Tarka Chat is a pretty good library for plugging in a chatbot. And it could be a whole lot better with more developer feedback. So, here is the Github link to it in case you want to give it a try (I hope you do) — https://github.com/tarkalabs/tarka-chat