Switching from Intercom to Tawk.to for Cost-Effective Customer Support — React Native

Abafor Chima
abaforchima
Published in
7 min readAug 15, 2023

--

As a part of a dynamic tech-driven company, our commitment to exceptional customer support has always been unwavering. We started of using Intercom for our Customer Support and it worked really fine. It still works very well and they have a lot of advanced features like Product Tours, Surveys, Custom Bots, Custom Answers etc. All these features are very good. However, as operational costs kept escalating, it was clear that we needed to find innovative solutions to ensure cost-effectiveness without compromising the quality of service. This is how we switched from Intercom.com to Tawk.to, a free tool, all while retaining crucial user experience elements and achieving substantial cost savings.

Screenshot of Intercom Dashboard

Switching to Tawk.to

Tawk.to is a versatile live chat and messaging platform designed to enhance customer engagement and support on websites and apps. It offers real-time communication capabilities, enabling businesses to interact with their visitors, address queries, provide assistance, and build meaningful relationships.

In our quest for a more cost-effective solution, I had used a few other CRM tools like Zoho, FreshDesk, etc. but Tawk.to stands out for our use case. Tawk.to — a powerful customer support platform that was not only free but also offered the flexibility to customize according to our requirements. We were excited about the possibilities that tawk.to presented, but the challenge was to ensure a seamless transition for our users.

As someone deeply involved in the transition process, retaining the user experience that our customers were accustomed to became a priority. This means that we needed to get some user data which are not embedded by default because Tawk.to doesn’t have an Android, iOS or React Native SDK. We wanted to ensure that their journey on the new platform felt as familiar as it did on Intercom.com. So we have to pass these data through the Tawk.to API in our React Native App.

Screenshot of Xend Support with User Attributes

Tawk.to JavaScript API

The Tawk.to JavaScript API offers a versatile collection of methods for integration into web projects, enabling customizable and interactive communication features through the Tawk.to platform. Through Tawk.to’s API, we were able to effortlessly carry over data to Tawk.to CRM.

Since Tawk.to does not have a React Native library at the moment, (maybe I’ll write one 😂) it has to be integrated using Javascript Injection into a React Native Webview. This is a hack in React Native to communicate with a webpage to inject and execute custom Javascript code within a WebView component in a React Native application. JavaScript injection in a React Native WebView can be used for various purposes, such as enhancing the functionality of the web content being displayed or interacting with the web page’s DOM elements.

Here’s an example of how JavaScript injection might look in a React Native WebView:

import React from 'react';
import { WebView } from 'react-native';

const MyWebView = () => {
const injectedJavaScript = `
// Injected JavaScript code
console.log("Hello from injected JavaScript!");
`;

return (
<WebView
source={{ uri: 'https://example.com' }}
injectedJavaScript={injectedJavaScript}
onMessage={(event) => {}}
/>
);
};

export default MyWebView;

In this example, the injectedJavaScript variable contains the JavaScript code that will be injected and executed within the WebView component when the web page loads. Now you know how the Injected Javascript works. You can read about it from the React Native Docs. Also, I noticed that if you don’t add onMessage function, the injectedJavascript doesn’t get called on iOS.

To add this successfully in to your app create two (2) files:
- chat.ts
- chatModal.tsx

The first file chat.ts is loaded to ensure that all async functions run properly and useData is loaded, and prepare the javascript to inject before navigating to chatModal.tsx which will actually load the Tawk.to chat and inject the javascript into it. Here is my code snippet for this. You can change the variables for your own app.

//chat.ts

import { getFromAsyncKeyOnly, getLoggedInUserFromReducer, isAndroid, isObjectEmpty } from '../../../utils/Helper';
import DeviceInfo from 'react-native-device-info';
import storeConstant from '../../../storage/StoreConstants';
import cryptoJs from 'crypto-js';
import moment from 'moment';

const startChat = (navigation) => {

var androidDevice = isAndroid() ? DeviceInfo.getBrand() + "-" +DeviceInfo.getModel() : "N/A";
var androidModel = isAndroid() ? "Android - "+ DeviceInfo.getSystemVersion() : "N/A";
var iOSDevice = isAndroid() ? "N/A": DeviceInfo.getBrand() + " - " + DeviceInfo.getDeviceId();
var iOSModel = isAndroid() ? "N/A" : "iOS - " + DeviceInfo.getSystemVersion();

tawktoUser().then((tawkUser) => {
var user = tawkUser;
console.log(user, androidDevice, androidModel, iOSDevice, iOSModel);

/*Here the important attributes which you want to store on the Tawk.to CRM
are added. It is important to pass attributes like device type and phone
model, so that it help the tech support to debug issues that may be peculiar
to a particular device when the customer complains.
*/
const jsCode = `
if (typeof Tawk_API !== "undefined") {
window.Tawk_API.onLoad = function(){
window.Tawk_API.setAttributes({
'name' : '${user?.fullName}',
'email' : '${user?.email}',
'userId': '${user?._id}',
'Phone': '${user?.phoneNo}',
'signedUpAt': '${moment(new Date(user?.createdAt).getTime()).format("DD MMM yyyy hh:mm")}',
'username': '${user?.username}',
'andCodePushNumber': '${isAndroid() ? 'CPV - '+ user?.codePushVersion : "N/A"}',
'androidDevice': '${androidDevice}',
'androidModel': '${androidModel}',
'iOSCodePushNumber': '${isAndroid() ? "N/A": 'CPV - '+ user?.codePushVersion }',
'iOSDevice': '${iOSDevice}',
'iOSModel': '${iOSModel}',
'hash' : '${getHash(user?.email)}'
}, function(error){
// if (error != undefined){
// alert(error);
// }
});
};
} else {
console.error("Tawk_API is not available. Make sure Tawk.to is properly loaded.");
}`;

navigation.navigate("ChatModal", {script: jsCode}) //using react navigation. Yours may differ
})
};

async function tawktoUser(){
var user = getLoggedInUserFromReducer().user;

if (user == null) {
user = {};
user.fullName = "Guest"
}
user.codePushVersion = await getFromAsyncKeyOnly(storeConstant.CODE_PUSH_VERSION);
return user;
}

function getHash(message){
var hash = "";
if(message != null || message != undefined){
hash = cryptoJs.HmacSHA256(
message,
"YOUR_TAWK.TO_API_KEY",
).toString(cryptoJs.enc.Hex);
}
//console.log(hash); //Check that it is hashed properly.
return hash;
}

const ChatHelper = {
startChat
}

export{ChatHelper};

Now that is done, modify the chatModal.tsx code below to suit your use case.

//chatModal.tsx

import React, { useContext, useRef } from 'react';
import {
StyleSheet,
View,
Dimensions,
Image,
TouchableOpacity,
ActivityIndicator,
Text
} from 'react-native';
import {WebView} from 'react-native-webview';
import * as AppImage from 'constants/image';
import { appBaseTheme, theme2 } from '../../../style/colors';
import { isAndroid } from '../../../utils/Helper';
import { FONTFAMILYMEDIUM } from '../../../fonts/Fonts';
import { fontsize } from '../../../style/fontsize';
import { ThemeContext } from '../../../context/ThemeContext';

const ChatModal = ({route, navigation}) => {
const [loading, setLoading] = React.useState(true);
const webviewRef = useRef();
const {script} = route.params

const {theme} = useContext(ThemeContext);
let appColorTheme = theme2[theme.mode];

return (
<>
<View style={[styles.modalStyle]}>
<View style={{flexDirection: "row", justifyContent: "space-between"}}>
<TouchableOpacity
activeOpacity={0.8}
onPress={() => navigation.goBack()}
style={styles.absoluteClose}>
<Image
source={AppImage.closeIcon} //To close the web modal
style={{height: 24, resizeMode: 'contain'}}
/>
</TouchableOpacity>

<Text style={styles.title}> Customer Support</Text>
<View></View>

</View>

<View style={styles.container}>
<WebView
ref={webviewRef}
onLoad={() => setLoading(false)}
javaScriptEnabled={true}
domStorageEnabled={true}
injectedJavaScript={script}
onMessage={(event) => {}}
source={{uri: `YOUR_TAWK.TO_URL`}}
/>
{loading ? ( <View style={styles.activityIndicatorStyle}>
<ActivityIndicator color={appColorTheme.PRIMARY} size="large" />
</View>) : null}
</View>
</View>
</>
);
};

export default ChatModal;

const screenHeight = Dimensions.get('screen').height;

const styles = StyleSheet.create({
container: {
marginTop: 0,
flex: isAndroid()? 0.85 : 0.93,
backgroundColor: appBaseTheme.BUTTONTEXT
},
absoluteClose: {
marginTop: isAndroid()? 10:60,
flexDirection: 'row',
justifyContent: 'flex-start',
marginLeft: 20,
marginVertical: 10,
},
modalStyle: {
marginTop:0,
height: screenHeight,
backgroundColor: appBaseTheme.BUTTONTEXT
},
activityIndicatorStyle: {
flex: 1,
position: 'absolute',
marginLeft: 'auto',
marginRight: 'auto',
marginTop: 'auto',
marginBottom: 'auto',
left: 0,
right: 0,
top: 0,
bottom: 0,
justifyContent: 'center',
},
title: {
fontFamily: FONTFAMILYMEDIUM,
fontSize: fontsize.HEADER4,
marginTop: isAndroid()? 10: 60,
marginLeft: -40
}
});

Reflecting on this, I’m proud of the seamless transition we accomplished. The shift from Intercom.com to Tawk.to was very much needed to save costs. If the Intercom costs were cheaper we’d have used it, or maybe we are not getting the amount of value we they hoped that we should get to pay the relatively “higher” fees.

The decision to transition from Intercom.com to tawk.to wasn’t just about cost savings; it was about embracing an opportunity for growth. With tawk.to’s free offering and the ability to add paid add-ons that aligned with our needs, we achieved significant operational cost reductions. By implementing these code changes, we were able to reduce spend on customer support tool from about $700/month to just $20/month.

So now what do you think? You tell me in the comment section. I have been a fan of Intercom. While Intercom’s offerings shine, the decision to opt for their services should be guided by a thorough evaluation of the value proposition against the backdrop of costs. In the competitve landscape where the alternatives also stand tall, the discerning eye of a business must assess whether the extra investment aligns with the desired outcomes. Maybe when the poor breathes again, we can go back to Intercom — or not. One thing remains clear: the heart of the matter lies in delivering value to customers and the business itself. So if the customers are satisfied with the communication channel, is there any incentive to do more?

I really want to know what you think about this. Please share your thoughts in the comments below. Thanks 🙏

--

--

Abafor Chima
abaforchima

Just my opinion about stuff, mostly tech stuff. Take it with a pinch of salt.