In a traditional multi-page website, the browser reloads the document whenever a visitor opens another page. A single-page application works differently: it loads the main document once and changes the route and page content with JavaScript.
This means that the HelpCrunch installation code should also run only once. You do not need to reinitialize the widget or resend the same user data after every route change. Instead, connect the HelpCrunch JS API methods to your app's authentication state and router events.
Table of contents:
- Before you start
- Install the widget once in the application shell
- Choose when to identify logged-in users
- What to do after a client-side route change
- Update user data only when it changes
- Log the user out of the widget
- Control the widget on different SPA routes
- Wait until HelpCrunch is ready when necessary
- Common SPA installation mistakes
- Testing checklist
Before you start
Copy your widget code from Settings → Channels → Website Widgets → Your_widget_name → Setup in your HelpCrunch account.
You will need:
-
Your HelpCrunch organization name
-
The widget
appIdfrom the copied installation code -
Access to the top-level HTML template or root layout of your SPA
-
Access to your app's authentication and router events if you want to identify users or change widget behavior between routes
1. Install the widget once in the application shell
Add the complete widget code copied from HelpCrunch before the closing </body> tag in the main HTML file of your SPA. Depending on your framework, this can be index.html, the root layout, or another component that remains mounted for the entire browser session.
Do not add the installation code to an individual routed page or component. Otherwise, your framework may execute or mount it again whenever that route is opened.
Your widget settings will look similar to this:
<script>
window.helpcrunchSettings = {
organization: '<your-organization>',
appId: '<your-appId>'
};
</script>
<!-- Paste the remaining widget code copied from HelpCrunch below. -->
When appId is present in window.helpcrunchSettings, the widget is initialized and displayed automatically. Keep the main widget loader from your HelpCrunch account unchanged.
If your framework uses server-side rendering, make sure the widget code runs only in the browser, where window and document are available.
Important: Treat widget initialization as a once-per-document action. Client-side route changes do not require another init call.
2. Choose when to identify logged-in users
The right setup depends on when your application receives the user's data.
Option A: initialize the widget with user data
Use this option when the authenticated user's data is already available before the widget is initialized.
<script>
window.helpcrunchSettings = {
organization: '<your-organization>',
appId: '<your-appId>',
user: {
user_id: '<unique-user-id>',
name: 'Jane Doe',
email: '[email protected]',
phone: '+12025550123',
company: 'Example Inc.',
custom_data: {
subscription: 'pro',
is_active: true
}
},
signature: '<signature-from-your-backend>'
};
</script>
<!-- Paste the remaining widget code copied from HelpCrunch below. -->
The signature is required only if the corresponding security option is enabled for your widget.
Option B: initialize the widget for a visitor, then authorize the user
Use this option if the widget should be available on public routes and a visitor can log in without a page reload.
Install the widget normally with organization and appId. Once login succeeds and the user's data becomes available, call userAuth:
function identifyHelpCrunchUser(user, signature) {
HelpCrunch(
'userAuth',
{
user_id: String(user.id),
name: user.name,
email: user.email,
phone: user.phone,
company: user.company
},
signature
);
}
If a signature is not enabled, omit the signature argument.
The userAuth method is especially useful in SPAs because it lets you authorize a user after the widget has already been initialized. You do not need to reload the page or load the widget code again.
Option C: wait for user data before initializing the widget
Use this option if you do not want to initialize HelpCrunch for an anonymous visitor while your app is still checking the session.
First, add only the organization name to the settings and keep the main widget loader from your HelpCrunch installation code:
window.helpcrunchSettings = {
organization: '<your-organization>'
};
Do not pass appId at this stage. After your app receives the user data, initialize and display the widget manually:
async function startHelpCrunch(user, signature) {
HelpCrunch('init', '<your-organization>', {
appId: '<your-app-id>',
user: {
user_id: String(user.id),
name: user.name,
email: user.email,
phone: user.phone,
company: user.company,
custom_data: {
subscription: user.subscription,
is_active: user.isActive
}
},
signature
});
HelpCrunch('showChatWidget');
}
If anonymous visitors should be able to use the widget while the session is being checked, use Option B instead.
User authentication requirements
Keep the following requirements in mind when enabling User Authentication mode:
-
user-idmust be a string and must be unique to one user -
Do not assign the same
user-idto different accounts -
Generate authentication hashes or signatures on your backend – never expose the secret or hash salt in frontend code
-
Include
user-idwhen you want to authenticate the user and keep their chat history consistent across sessions and devices -
If you send only a name or email without
userId, the user is not authenticated; these values only prefill the pre-chat form -
The pre-chat form is not displayed for a user authenticated through User Authentication mode
3. What to do after a client-side route change
The HelpCrunch widget remains initialized when your router changes the URL. Its current conversation and identified user also remain available.
Therefore, do not call init, userAuth, or updateUser after every navigation if the user and their data have not changed.
Use a router hook only for behavior that actually depends on the new route, for example:
-
Show or hide the widget
-
Switch the widget localization
-
Track that the user opened a particular app section
-
Update a custom attribute such as the current product area
-
Open the chat or trigger a specific message after a route-based action
4. Update user data only when it changes
SPA navigation does not require you to resend the same contact information. Call the corresponding method when your application actually changes a value.
Update standard user attributes
Use updateUser when the user changes their name, email, phone number, or company. You can send only the fields that changed.
HelpCrunch('updateUser', {
email: '[email protected]'
});
If your security configuration requires a signature, pass a fresh signature generated by your backend according to your User Authentication mode setup.
Update custom user attributes
Use updateUserData for product- or business-specific data:
HelpCrunch('updateUserData', {
subscription: 'enterprise',
projects_count: 12,
trial_ends_at: '2026-09-01T10:00:00+00:00'
});
Create the corresponding attributes first in Settings → Contacts → Custom attributes. Custom data can contain integer, float, string, URL, boolean, and DateTime values. Dynamic custom data updates work only for a user who has a user_id.
Use custom attributes for the latest known state. If you need a chronological record of navigation or actions, use trackEvent instead of repeatedly overwriting a custom attribute.
Avoid sending authentication tokens, passwords, sensitive personal data, or URL query parameters that may contain private information.
5. Log the user out of the widget
Clearing your app's local user state does not automatically log the user out of HelpCrunch. Call the logout method when the user logs out of your SPA:
HelpCrunch('logout', function (data) {
if (data?.success) {
// The HelpCrunch session has been cleared.
}
});
This is especially important if several users can use the same browser or if your product supports switching between accounts. It prevents one user's chat history and contact data from appearing to another user.
When switching directly from one account to another, use this sequence:
-
Call
logoutfor the current HelpCrunch user. -
Wait for the successful callback.
-
Call
userAuthwith the new user's unique user_id and security signature, if enabled.
Do not call userAuth for the second account without logging out the first one.
6. Control the widget on different SPA routes
Use the HelpCrunch JS API methods from your router or component event handlers:
HelpCrunch('showChatWidget'); // Show the widget button
HelpCrunch('hideChatWidget'); // Hide the widget button
HelpCrunch('openChat'); // Open the chat panel
HelpCrunch('closeChat'); // Close the chat panel
hideChatWidget hides the widget button. If the open chat panel should also disappear when the user enters a restricted route, call closeChat before hideChatWidget.
For route-specific buttons, call the methods directly from your framework's click handler:
function contactSupport() {
HelpCrunch('typeUserMessage', 'I need help with my subscription');
HelpCrunch('openChat');
}
For SPA navigation, API methods are more reliable than adding ?HelpCrunchOpenChat=1 or ?HelpCrunchInputText=... to a URL because the latter are primarily processed when a page is loaded. A client-side route change may not repeat page-load logic.
If you use URL-based widget visibility, auto-message, or popup rules configured in HelpCrunch, test both direct page loads and client-side transitions. When the behavior must change immediately after every route transition, call the corresponding JS API method from your router hook.
7. Wait until HelpCrunch is ready when necessary
The HelpCrunch installation snippet creates a command queue, so API calls made before initialization can run after init finishes. If your code must perform an action only after the main script is loaded, use onScriptLoaded:
HelpCrunch('onScriptLoaded', () => {
// HelpCrunch-dependent logic
});
If you are building a custom launcher and need the initialized widget to be ready, use onReady:
HelpCrunch('onReady', () => {
// Display or enable your custom chat button
});
Register these handlers once in your top-level integration, not after every route change.
Common SPA installation mistakes
-
Adding the full widget snippet to every routed component
-
Calling
initafter every navigation -
Calling
userAuthrepeatedly even though the current user has not changed -
Passing user_id as a number instead of a string
-
Reusing the same user_id for different users
-
Generating a security signature or storing the hash salt in frontend code
-
Updating custom data before creating the attributes in HelpCrunch
-
Calling
updateUserDatafor an anonymous visitor without user_id -
Clearing the app session without calling
HelpCrunch('logout') -
Authorizing a second account before logging the first HelpCrunch user out
-
Expecting
window.onloadto run after a client-side route transition -
Registering duplicate router or click listeners after every render
Testing checklist
After implementing the widget, test the following scenarios:
-
Open a public route with a full page load and confirm that the widget appears.
-
Navigate between routes without reloading the page and confirm that the widget remains initialized.
-
Open a deep link directly and confirm that route-specific visibility and localization are correct.
-
Log in without reloading the page and confirm that the correct user appears in Contacts.
-
Change the user's email or a custom attribute and confirm that the profile is updated.
-
Switch languages and confirm that the widget localization changes.
-
Visit routes where the widget should be hidden and confirm that both the button and, if required, the open panel disappear.
-
Use the browser's Back and Forward buttons and confirm that your router hook runs once per navigation.
-
Log out, then log in as another user and confirm that the two users' conversations and data remain separate.
-
Check that the HelpCrunch widget script is loaded only once during a client-side session.