Global script in the app shell, not page-by-page embeds
Developer quickstarts
Use the v1 script once, then send safe page context from the app screens where users need help.
Global script in the app shell, not page-by-page embeds
Path, title, feature, workflow, role, locale
Loaded, origin allowed, route allowed, context received
path, title, feature, workflow, role, and locale
passwords, tokens, payment data, emails, phone numbers, raw customer records
public widget key and optional script URL only; never service accounts or private API keys
user upload or paste only; no automatic page capture or DOM scraping
widget loaded, origin allowed, route allowed, context received
Private rollout workspaces can use the exact dashboard snippet immediately. AnswerLattice supports the stable v1 script URL and browser global for client installs.
Environment setup
Put only the public AnswerLattice widget key and optional script URL in client-safe env variables. AnswerLattice does not need your Firebase credentials, service account, tenant IDs, store IDs, or user data inside the browser app.
NEXT_PUBLIC_ANSWERLATTICE_WIDGET_KEY=al_your_widget_key
NEXT_PUBLIC_ANSWERLATTICE_WIDGET_SCRIPT_SRC=https://answerlattice.com/widget/v1/answerlattice-widget.jsVITE_ANSWERLATTICE_WIDGET_KEY=al_your_widget_key
VITE_ANSWERLATTICE_WIDGET_SCRIPT_SRC=https://answerlattice.com/widget/v1/answerlattice-widget.jsNUXT_PUBLIC_ANSWERLATTICE_WIDGET_KEY=al_your_widget_key
NUXT_PUBLIC_ANSWERLATTICE_WIDGET_SCRIPT_SRC=https://answerlattice.com/widget/v1/answerlattice-widget.jsLoad the widget once in your app shell and send route context from a small client component.
'use client';
import Script from 'next/script';
import { usePathname } from 'next/navigation';
import { useCallback, useEffect } from 'react';
export function AnswerlatticeInstall() {
const pathname = usePathname() || '/';
const widgetKey = process.env.NEXT_PUBLIC_ANSWERLATTICE_WIDGET_KEY;
const updateContext = useCallback(() => {
if (!widgetKey) return;
window.AnswerlatticeWidget?.page({
path: pathname,
title: document.title,
feature: pathname.split('/').filter(Boolean)[0] || 'app',
role: 'member',
locale: navigator.language || 'en',
});
}, [pathname, widgetKey]);
useEffect(() => {
updateContext();
}, [updateContext]);
if (!widgetKey) return null;
return (
<Script
id="answerlattice-widget"
src="https://answerlattice.com/widget/v1/answerlattice-widget.js"
data-answerlattice-key={widgetKey}
strategy="afterInteractive"
onLoad={updateContext}
/>
);
}Initialize once, then call page() from your router or product screen component.
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
function loadAnswerlattice(widgetKey, updateContext) {
const existing = document.querySelector('script[data-answerlattice-widget="v1"]');
if (existing) {
if (window.AnswerlatticeWidget) updateContext();
else existing.addEventListener('load', updateContext, { once: true });
return () => existing.removeEventListener('load', updateContext);
}
const script = document.createElement('script');
script.src = 'https://answerlattice.com/widget/v1/answerlattice-widget.js';
script.async = true;
script.setAttribute('data-answerlattice-widget', 'v1');
script.setAttribute('data-answerlattice-key', widgetKey);
script.addEventListener('load', updateContext, { once: true });
document.head.appendChild(script);
return () => script.removeEventListener('load', updateContext);
}
export function AnswerlatticeInstall() {
const location = useLocation();
const widgetKey = import.meta.env.VITE_ANSWERLATTICE_WIDGET_KEY;
useEffect(() => {
if (!widgetKey) return;
const updateContext = () => window.AnswerlatticeWidget?.page({
path: location.pathname,
title: document.title,
feature: location.pathname.split('/').filter(Boolean)[0] || 'app',
role: 'member',
locale: navigator.language || 'en',
});
const removeLoadListener = loadAnswerlattice(widgetKey, updateContext);
updateContext();
return removeLoadListener;
}, [location.pathname, widgetKey]);
return null;
}Use the same safe page context from mounted route components.
import { onMounted, onUnmounted, watch } from 'vue';
import { useRoute } from 'vue-router';
export function useAnswerlatticeInstall(widgetKey: string) {
const route = useRoute();
let removeLoadListener = () => {};
const updateContext = () => {
if (!widgetKey) return;
window.AnswerlatticeWidget?.page({
path: route.path,
title: document.title,
feature: route.path.split('/').filter(Boolean)[0] || 'app',
role: 'member',
locale: navigator.language || 'en',
});
};
onMounted(() => {
if (!widgetKey) return;
let script = document.querySelector<HTMLScriptElement>('script[data-answerlattice-widget="v1"]');
if (!script) {
script = document.createElement('script');
script.src = 'https://answerlattice.com/widget/v1/answerlattice-widget.js';
script.async = true;
script.setAttribute('data-answerlattice-widget', 'v1');
script.setAttribute('data-answerlattice-key', widgetKey);
document.head.appendChild(script);
}
if (!window.AnswerlatticeWidget) {
script.addEventListener('load', updateContext, { once: true });
removeLoadListener = () => script.removeEventListener('load', updateContext);
}
updateContext();
});
onUnmounted(() => removeLoadListener());
watch(() => route.path, updateContext);
}Paste the script and call the runtime directly when route context changes.
<script src="https://answerlattice.com/widget/v1/answerlattice-widget.js" data-answerlattice-key="al_your_widget_key" async></script>
<script>
window.addEventListener("load", function () {
window.AnswerlatticeWidget?.page({
path: window.location.pathname,
title: document.title,
feature: "billing",
workflow: "manage_subscription",
role: "member",
locale: navigator.language || "en"
});
});
</script>The Widget screen checks that the key exists, script loaded, origin is valid, route is allowed, and page context arrived.