Voicegram

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

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:

ts
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:

ts
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:

ts
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).

ts
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

tsx
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

ts
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

ts
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

Need help? Email support@voicegram.io.