Programmatic API
Open, close, and toggle the widget from JavaScript. Useful for single-page apps, conditional triggers, and integration with your own UI framework.
On this page
- When to reach for the JS API
- Public surface
- Detect when the widget is ready
- open() behavior
- show() / hide() / toggle() control the bubble, NOT the modal
- Imperative hide() survives route changes
- destroy()
- setZIndex(value)
- Examples
- Open the widget from a custom React button
- Close the widget on route change in a SPA
- Toggle the bubble based on app state
- Next steps
For most integrations, the data-voicegram-trigger attribute is enough: any HTML element with the attribute becomes a button that controls the widget. For everything else, the widget exposes a small JavaScript API on window.__voicegram__.
When to reach for the JS API
You usually do not need this. Reach for it when:
- You need to open the widget in response to a non-click event (a timer, a route change, a third-party SDK callback).
- You are inside a component framework where attaching an HTML attribute is awkward.
- You want to react to widget readiness in your app's bootstrap code.
- You want to tear down the widget entirely on certain pages of a single-page app.
Public surface
After the widget script loads and initializes, the widget exposes itself globally:
window.__voicegram__.open() // Open the modal at the email step
window.__voicegram__.close() // Close the modal (cancels in-progress recording)
window.__voicegram__.show() // Show the floating bubble (clears imperative-hide flag)
window.__voicegram__.hide() // Hide the floating bubble (persists across SPA route changes)
window.__voicegram__.toggle() // Toggle bubble visibility
window.__voicegram__.destroy() // Tear down the widget completely
window.__voicegram__.setZIndex(value) // Override the widget's z-index
A voicegram:ready CustomEvent fires on window exactly once, right after __voicegram__ is exposed.
Detect when the widget is ready
The widget script loads asynchronously. Your code may run before or after init completes. Use this pattern to handle both cases:
function onWidgetReady() {
// Widget is initialized. Safe to call __voicegram__.open() etc.
console.log('Voicegram ready');
}
// Fast path: widget already initialized.
if (typeof window.__voicegram__?.open === 'function') {
onWidgetReady();
} else {
// Otherwise, wait for the ready event (fires exactly once).
window.addEventListener('voicegram:ready', onWidgetReady, { once: true });
}
Prefer the event over polling
The voicegram:ready event fires exactly once at init. Listen for it rather than polling window.__voicegram__ on a setInterval. The fast-path check above covers the case where your listener attaches after init.
open() behavior
open() opens the modal at the email-input step. It has three guards:
- No-op when the user is mid-flow (mid-recording, mid-verification). This protects an in-progress session from being interrupted.
- No-op when the domain is server-disabled. If your account or this specific domain is disabled in the dashboard,
open()does nothing. This means a disabled customer's site cannot summon the modal even through JavaScript. - Safe to call early. You can safely call
open()early; it opens with the correct theme once configuration has loaded.
show() / hide() / toggle() control the bubble, NOT the modal
This is the most common point of confusion:
show(),hide(),toggle()control the floating bubble's visibility.open(),close()control the modal.
If you want a button that opens the modal, that is open() (or data-voicegram-trigger). Do not use show() to open the modal.
Imperative hide() survives route changes
hide() sets a flag that persists across SPA navigation. The bubble stays hidden until you call show(). The page-level <meta name="voicegram-bubble"> is also still respected. Both signals combine with logical OR:
window.__voicegram__.hide();
// Navigate to another page in the SPA. Bubble stays hidden.
// On a page with `<meta name="voicegram-bubble" content="off">`,
// bubble also stays hidden (would have been hidden anyway).
window.__voicegram__.show();
// Bubble shows again on pages where the meta does not suppress it.
destroy()
destroy() tears down the widget and removes its DOM. Calling init() again afterward re-creates it.
Use case: a single-page app where the widget should not persist across certain routes. (Usually unnecessary. The per-page meta tag is simpler.)
setZIndex(value)
Override the widget's CSS z-index. Useful when the widget's bubble or modal collides with your own UI (modals, drawers, toasts).
window.__voicegram__.setZIndex(999999);
The default z-index is set high enough to layer over most page chrome, but if your app uses its own very-high z-index values, you can push the widget above them with this call.
Examples
Open the widget from a custom React button
function ContactButton() {
return (
<button
type="button"
onClick={() => {
if (typeof window.__voicegram__?.open === 'function') {
window.__voicegram__.open();
}
}}
>
Leave us a voicegram
</button>
);
}
(For most cases, data-voicegram-trigger on the button is simpler and gets you keyboard accessibility for free.)
Close the widget on route change in a SPA
import { useEffect } from 'react';
import { usePathname } from 'next/navigation';
function CloseWidgetOnRouteChange() {
const pathname = usePathname();
useEffect(() => {
window.__voicegram__?.close?.();
}, [pathname]);
return null;
}
Toggle the bubble based on app state
import { useEffect } from 'react';
function ToggleBubble({ visible }: { visible: boolean }) {
useEffect(() => {
const apply = () => {
if (visible) window.__voicegram__?.show?.();
else window.__voicegram__?.hide?.();
};
if (window.__voicegram__) apply();
else window.addEventListener('voicegram:ready', apply, { once: true });
}, [visible]);
return null;
}
Next steps
- Customization. The HTML-attribute-only approach for most cases.
- Framework recipes. Drop-in install snippets for common frameworks.
- Troubleshooting. Solutions for the most common issues.