Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement →
Sign In

ahize

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ahize

One unified API for 18 live-chat & customer-support widgets — Intercom, Crisp, Tawk.to, Zendesk, HubSpot, Chatwoot, LiveChat, Drift, Freshchat, Olark, Userlike, HelpScout, Smartsupp, LiveAgent, Gist, JivoChat, Tidio, Sendbird. Zero deps, tree-shakeable, S

latest
Source
npmnpm
Version
0.2.1
Version published
Weekly downloads
65
35.42%
Maintainers
1
Weekly downloads
 
Created
Source


ahize

ahize

One unified API for 18 live-chat & customer-support widgets.
Zero dependencies. Tree-shakeable. SSR-safe. Strict TypeScript.

npm version npm downloads bundle size license sponsors

Why

Every live-chat vendor ships its own snippet, its own globals, its own quirks. Wrappers exist for individual ones (react-use-intercom, react-zendesk, tawk-messenger-react, …) — but each pulls in a framework, locks you to one provider, and re-introduces the bugs the underlying snippet already had: SSR crashes, calls dropped before boot, HMAC fields silently missing, no shutdown→boot reversibility.

ahize is one zero-dependency layer over all of them. Same surface, swap providers by changing the import path. Pre-boot calls are buffered. Identity verification is typed. SSR is a no-op. CSP is documented per provider.

Today you're on Intercom:

import * as chat from "ahize/intercom"

await chat.load({ appId: "abc" })
chat.identify({ id: "u1", email: "ada@example.com" })
chat.track("plan_upgraded", { tier: "pro" })
chat.show()

Tomorrow you switch to Crisp — only the import path and the load() options change:

import * as chat from "ahize/crisp"

await chat.load({ websiteId: "uuid" })
chat.identify({ id: "u1", email: "ada@example.com" })
chat.track("plan_upgraded", { tier: "pro" })
chat.show()

Install

npm install ahize
# pnpm add ahize / yarn add ahize / bun add ahize

Hello, world

import { load, identify, track, show } from "ahize/intercom"

await load({ appId: "abc123" })
await identify({
  id: "user_1",
  email: "ada@example.com",
  name: "Ada Lovelace",
})
await track("plan_upgraded", { tier: "pro" })
await show()

That's it. The Intercom CDN is injected, boot is fired, and your identify/track/show calls were buffered and drained in order.

The unified surface

Every provider exports the exact same functions:

load(options); // inject CDN & boot — Promise resolves when widget API attaches
identify(identity); // set user; verification: hmac | jwt | callback
track(event, metadata?); // emit a custom event; generic <T extends EventMetadata>
pageView({ path, locale }); // notify on SPA route change
show();
hide(); // toggle widget visibility
shutdown(); // end session — keeps config so you can re-identify
destroy(); // hard reset — removes script, globals, listeners
ready(); // Promise<void> resolved once the real API is live
isReady();
state(); // synchronous lifecycle ('idle'|'loading'|'ready'|'shutdown')
getIdentity();
onIdentityChange(cb); // typed identity transitions

If you call any method before load() resolves, the call is queued and flushed in order once the provider is ready. No more "Intercom is not defined" warnings.

On top of the unified surface, most providers expose a typed on(event, handler) for their documented lifecycle events (widget open/close, message sent/received, conversation started, unread count, …) and a handful of vendor-native methods — see the Providers table for the per-provider extras.

Identity verification

Most providers want a server-issued HMAC or JWT to prevent users from spoofing each other's profiles. ahize makes it a typed required field:

// Intercom — HMAC of user_id with your app secret
await identify({
  id: "user_1",
  email: "ada@example.com",
  verification: { kind: "hmac", hash: process.env.INTERCOM_USER_HASH! },
})

// HubSpot — JWT
await identify({
  id: "user_1",
  email: "ada@example.com",
  verification: { kind: "jwt", token: serverIssuedJwt },
})

// Zendesk Messenger — callback for token refresh on 401
await identify({
  id: "user_1",
  verification: {
    kind: "callback",
    getToken: async () => fetchFreshJwtFromServer(),
  },
})

Pass the wrong kind for a provider and you get a typed rejection — no silent drop. See ahize/capabilities for who supports what.

SSR

ahize is safe to import from any server runtime. Every method short-circuits when window/document are unavailable, so this works in Next.js App Router, Nuxt 4, Remix, SvelteKit, Astro, and Cloudflare Workers without guards.

// app/layout.tsx — no "use client" needed for the import itself
import { load } from "ahize/intercom"

await load({ appId: "..." }) // resolves to undefined on the server, no-op

If you want to be belt-and-braces sure no DOM code enters your SSR bundle, import the matching no-op stub:

import { load, identify, track } from "ahize/server"

Framework adapters

Each adapter is framework-agnostic — bring your own React/Vue/Angular, no peer dependencies.

Next.js (App Router)

"use client"
import * as React from "react"
import * as intercom from "ahize/intercom"
import { createAhizeComponent } from "ahize/next"
import { usePathname, useSearchParams } from "next/navigation"

const Ahize = createAhizeComponent(React, { usePathname, useSearchParams })

export default function ChatBoot() {
  return <Ahize provider={intercom} options={{ appId: "abc123" }} autoPageView />
}

Mount once in your root layout. pageView() auto-fires on every route change — fixes HubSpot's targeting rules, keeps Intercom's session tracking accurate.

Nuxt 4 (and Nuxt 3)

// app/plugins/ahize.client.ts   (Nuxt 4 default srcDir)
// plugins/ahize.client.ts       (Nuxt 3, or Nuxt 4 with custom srcDir)
import { defineNuxtPlugin } from "#app"
import * as intercom from "ahize/intercom"
import { createNuxtAhizePlugin } from "ahize/nuxt"

export default defineNuxtPlugin(
  createNuxtAhizePlugin({
    provider: intercom,
    options: { appId: "abc123" },
    autoPageView: true,
  }),
)

The plugin & defineNuxtPlugin API is identical between Nuxt 3 and 4 — only the default source directory changed (app/ in Nuxt 4). Use $ahize from useNuxtApp() to access the provider in components.

React (any meta-framework)

import * as React from "react"
import * as crisp from "ahize/crisp"
import { createUseAhize } from "ahize/react"

const useAhize = createUseAhize(React)

function App() {
  const { isReady, identify, show } = useAhize({
    provider: crisp,
    options: { websiteId: "..." },
  })

  return (
    <button disabled={!isReady} onClick={() => show()}>
      Open chat
    </button>
  )
}

Vue 3, Svelte, SvelteKit, Remix, Astro, Angular

All shipped — see ahize/vue, ahize/svelte, ahize/sveltekit, ahize/remix, ahize/astro, ahize/angular. Same factory pattern: pass the framework primitives in.

load() never fires on import. Pair it with your CMP:

import * as intercom from "ahize/intercom"

OneTrust.OnConsentChanged(() => {
  if (OnetrustActiveGroups.includes("C0004")) {
    intercom.load({ appId: "abc123" })
  } else {
    intercom.destroy() // removes script, globals, cookies
  }
})

Defer strategies for LCP-sensitive pages:

load({ appId: "...", defer: "idle" }) // requestIdleCallback
load({ appId: "...", defer: "interaction" }) // first pointerdown/scroll/keydown
load({ appId: "...", defer: "manual" }) // never auto-injects
load({ appId: "...", consent: hasConsent }) // gate behind a flag

EU region selection is built in:

intercom.load({ appId: "...", region: "eu" }) // → api-iam.eu.intercom.io
hubspot.load({ portalId: "...", region: "eu1" }) // → js-eu1.hs-scripts.com

CSP

Strict CSP breaks every chat widget by default. ahize/csp ships the exact directive list per provider, including the WSS endpoints competitor wrappers always forget:

import { cspDirectives, mergeCsp, toHeaderString } from "ahize/csp"

const policy = mergeCsp(
  cspDirectives("intercom"),
  cspDirectives("crisp"),
  cspDirectives("chatwoot", { chatwootBaseUrl: "https://chat.acme.com" }),
)

response.setHeader("Content-Security-Policy", toHeaderString(policy))

Pass a nonce through load() and the same nonce through your CSP:

load({ appId: "...", nonce: cspNonce })

Want to catch violations in dev?

import { watchCspViolations } from "ahize/csp"

watchCspViolations((event) => {
  console.warn("CSP blocked", event.blockedURI, "for", event.violatedDirective)
})

Facade mode

For pages where chat is below the fold and LCP matters, mount a tiny launcher. The real provider boots on hover or click:

import * as intercom from "ahize/intercom"
import { mountFacade } from "ahize/facade"

mountFacade({
  provider: "intercom",
  boot: () => intercom.load({ appId: "abc123" }),
})

Under 2 KB. No CSS file, no framework. The launcher removes itself once the real widget is ready.

Switching providers

Capability matrix is queryable so you don't hard-code branches:

import { capabilities, supports } from "ahize/capabilities"

if (supports("zendesk", "callback")) {
  await identify({ id: "u1", verification: { kind: "callback", getToken } })
} else if (supports("zendesk", "jwt")) {
  await identify({ id: "u1", verification: { kind: "jwt", token } })
}

Diagnostics

When the snippet refuses to load, ahize/diagnostics probes the CDN and returns a hint:

import { diagnose } from "ahize/diagnostics"

const result = await diagnose("intercom", { appId: "abc123" })
//   { ok: false, status: 404, hint: "Snippet not found — typo in id…" }

Providers

Every provider ships the unified surface (load / identify / track / pageView / show / hide / shutdown / destroy / ready / isReady / state / getIdentity / onIdentityChange) plus a provider-specific extension: on(event, handler) typed event bridge, and vendor-native methods where they exist. Audited against live vendor docs 2026-04-16.

ProviderSub-pathIdentity / regionsProvider-specific extras
Intercomahize/intercomHMAC, JWT, us/eu/aushowSpace, showNewMessage, startTour/Survey/Checklist, showArticle/News/Ticket, getVisitorId, onShow/onHide/onUserEmailSupplied, 11 typed boot fields
Crispahize/crispHMAC, hot-reconfigureopen/close/toggle, sendMessage, helpdeskSearch, setSessionSegments, setUserAvatar, 13 events, runtime config
Tawk.toahize/tawkHMAC, login() restores historymaximize/minimize/popup, addTags/removeTags, getStatus/isChat*, visitor preload, 19 event hooks
Zendeskahize/zendeskJWT, callbackopen/close, setConversationTags, setCustomization, newConversation, resetWidget, 13 messenger:on events, cookies/zIndex config
Chatwootahize/chatwootHMAC, self-hosted, settingssetColorScheme, deleteAttribute, popoutChatWindow, setLocale, setBubbleVisibility, on(opened/closed/postback/…), 11 typed settings
HubSpotahize/hubspotIdentification token, na1/eu1/ap1on(conversationStarted/…) (8 events), status(), refresh, 7 typed config (cookie banner, inline embed, attachment, CSP)
LiveChatahize/livechatmaximize(draft?), minimize, hideGreeting, triggerSalesTracker, getState/CustomerData/ChatData, 10 events, 7 typed __lc fields
Freshchatahize/freshchatJWT, us/eu/in/auopen/close, setLocale, setTags/setFaqTags, setBotVariables, trackPage, isOpen/isLoaded, 16 events, 8 typed init fields
Olarkahize/olarkgetVisitorDetails(), sendMessage/NotificationTo*, setOperatorGroup, setLocale, 12 events, group/locale boot config
HelpScoutahize/helpscoutHMACsearch, article, sessionData, config, reset, toggle, askQuestion, showMessage, info, once, prefill(attachments), full BeaconConfig object
LiveAgentahize/liveagentself-hosted optaddUserDetail, addTicketField, setVisitorLocation, createForm, hasOpenedWidget, on(chatStarted/chatEnded/online/offline)
Gistahize/gistHMACopen/close, showLauncher/hideLauncher, navigate, showArticle, trigger, setSidebar/setStandard, 12 events
JivoChatahize/jivochatsetUserToken (verification)setClientAttributes (rate-limited), setCustomData, startCall, sendOfflineMessage, sendPageTitle, 12 events, sync chatMode/getUnreadMessagesCount/getUtm
Smartsuppahize/smartsuppopen/close, prefillMessage, sendMessage, setGroup, setLanguage, getVisitorId, on(messageSent/Received/messengerClose), 12 typed _smartsupp fields
Tidioahize/tidiotidioChatApi.track forwarding, setColorPalette, display, messageFromOperator/Visitor, addVisitorTags, setVisitorCurrency, 10 events, pre-load language/identify

Deprecated / sunset providers

Still functional but the underlying vendor has announced sunset / EOL. Wrappers emit a one-shot console.warn on first load() and are marked @deprecated in JSDoc. No new feature work planned.

ProviderSub-pathStatus
Driftahize/driftVendor sunset announced 2026-03-06 (Clari + Salesloft).
Sendbirdahize/sendbirdAI Chatbot Widget discontinued; repo archived 2025-07-09 at v1.9.7. Consider Sendbird Desk.
Userlikeahize/userlikev1 CDN EOL 2026-08-01. Vendor rebranded to Lime Connect; v2 is @userlike/messenger with a different surface.
Zendesk Classicahize/zendesk-classicLimited to Zendesk accounts created before 2023-06-05. Chat Web SDK removal started 2025-04-30. Use ahize/zendesk (Messenger).

Migrating

From react-use-intercom:

-import { IntercomProvider, useIntercom } from "react-use-intercom";
+import * as intercom from "ahize/intercom";
+import { createUseAhize } from "ahize/react";
+import * as React from "react";
+const useAhize = createUseAhize(React);

-<IntercomProvider appId="abc">{children}</IntercomProvider>
+const { isReady, identify, show, hide, shutdown } = useAhize({
+  provider: intercom,
+  options: { appId: "abc" },
+});
react-use-intercomahize
boot(props)load({ appId, ...props })
update(props)identify(props)
trackEvent(name, meta)track(name, meta)
shutdown()shutdown()
hardShutdown()destroy()
boot({ user_hash })identify({ verification: { kind: "hmac", hash } })
boot({ intercom_user_jwt })identify({ verification: { kind: "jwt", token } })

Same shape works for react-zendesk, tawk-messenger-react, @productdevbook/chatwoot, @livechat/widget-react. The notable change across all of them: ahize separates boot (load) from user identity (identify).

Playground

A plain Vite + TypeScript playground is checked into playground/ for trying a real widget in the browser without setting up a framework.

pnpm playground

That installs the playground's own deps (vite, typescript) and opens the dev server on http://localhost:5173. It imports the Chatwoot provider directly from ../src/providers/chatwoot.ts, so any code change in src/ reloads instantly — no build step. Paste your websiteToken (and baseUrl if self-hosted), hit load(), then play with identify/track/show/hide/setLocale and watch the event log.

Sponsors

Sponsors of productdevbook

If ahize saves you a few hours of live-chat integration pain, consider sponsoring on GitHub so work on the next provider + framework adapter keeps going.

Contributing

Issues and PRs welcome at github.com/productdevbook/ahize. Missing a provider? The pattern is small enough to copy from any existing one — src/providers/livechat.ts is a good minimal template.

Credits

ahize exists because every one of these libraries solved part of the problem and showed us the bugs to design around. Many of the design decisions in ahize are direct responses to issues filed on these projects — thank you to every maintainer and reporter.

Intercom

Crisp

  • crisp-im/crisp-sdk-web — official wrapper; our $crisp.push-only contract comes from issues filed against it

Tawk.to

Zendesk

HubSpot

Chatwoot

Performance & deferred load

LiveChat (text.com)

Other providers

  • userlike/messenger — Result<ok, err> pattern we propagate
  • HelpScout Beacon, Drift, Freshchat, Olark, Smartsupp, LiveAgent, Gist, JivoChat, Tidio, Sendbird — official docs & community wrappers

Other unified-chat work

If your library should be on this list and isn't, open an issue — happy to credit.

License

MIT © productdevbook

Keywords

ahize

FAQs

Package last updated on 16 Apr 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts