Skip to content

The web SDK puts Nexafon’s screens inside any web page as Web Components. Each one renders a frame of Nexafon’s embed pages, so your page needs no UI code and always shows the current version. It works in every framework; the React, Vue and Angular wrappers add idiomatic components on top.

With a script tag (the CDN serves the latest v1, with fixes and new features automatically):

<script src="https://app.nexafone.so/embed/v1/nexafon.js"></script>

Or from npm, as a typed loader that adds the same script once:

Terminal window
npm install @nexafon/js
import { loadNexafon } from '@nexafon/js'
// Resolves to null on the server (Next.js, Nuxt, Angular SSR, SvelteKit): it never touches window there.
const Nexafon = await loadNexafon()

Call init once per page, before or after the elements are in the DOM:

Nexafon.init({
publishableKey: 'pk_live_…',
getUserToken: async () => {
const r = await fetch('/nexafon-token', { method: 'POST' })
if (!r.ok) throw new Error(`The token endpoint answered ${r.status}`)
return r.text()
},
appearance: { primaryColor: '#16A34A', radius: 10, colorMode: 'auto' },
locale: 'en',
popOutCalls: false,
})
Option Type
publishableKey string pk_live_…/pk_test_… for one business, or a partner’s ppk_live_…/ppk_test_…
getUserToken () => Promise<string> | string Returns a fresh user token from your server each time; a token works once (user tokens). Throw when your server answers an error (such as 401 when no one is signed in), so the SDK never gets an error body as a token
appearance object Primary colour, radius, font and light/dark mode (appearance)
locale 'en' | 'so' | 'ar' The embedded screens’ language; Arabic renders right to left
popOutCalls boolean On multi-page sites, calls run in a small window so navigating does not end them

init loads a hidden hub frame at once: it holds the session, the one realtime connection and the call audio for the whole page, and every visible component shares it. So the first component appears in well under a second, and a page with an inbox, a softphone and five call buttons still opens one connection.

Element Shows Attributes
<nexafon-app> The whole dashboard with Nexafon’s side menu: inbox, calls, team, settings, billing, orders… (each section only for users whose role may open it) store
<nexafon-inbox> The shared WhatsApp inbox store
<nexafon-softphone> The softphone: incoming calls, dial pad, call controls store
<nexafon-call-button> A button that calls one customer on WhatsApp phone (E.164, required), store

store opens a partner’s store first, by its externalRef or workspace id (store switcher). Changing an attribute reloads that component.

Size <nexafon-app> and <nexafon-inbox> yourself (they fill their element, at least 480 px high); the softphone and the call button size themselves to their content. Frames get allow="microphone; autoplay; clipboard-write": the browser asks for the microphone the first time a call starts.

Without custom elements you can mount the same screens imperatively:

const unmount = Nexafon.mount(document.getElementById('support'), { component: 'inbox' })
// Nexafon.mount(el, { app: true }) for the whole dashboard; unmount() removes it again.
Method Does
openChat(phone) Opens (or starts) the chat with that customer in the inbox or app on the page
call(phone) Calls the customer on WhatsApp from the store the user viewed last; with popOutCalls the call opens in the pop-out window
startInternalCall(memberIds) Starts a team call with those members
setAppearance(appearance) Changes colours, radius, font or mode at once, for example when your own theme switches
on(event, listener) / off(event, listener) Listen to events; on returns a function that stops listening
destroy() Removes every frame and ends the session on this page

Methods that act through a frame return a Promise that rejects when the action is not possible, such as a call without a microphone.

On a classic multi-page site a page load would cut an active call. With popOutCalls: true a call opens in a small pop-out window that survives navigation; a call already live on the page can move into it and back. If the browser blocks the pop-up, the call stays on the page and the user is asked to allow pop-ups. The HTML example shows it across two pages.

  • The publishable key starts sessions only from its allowed origins, and Nexafon’s frames load only on the workspace’s embed domains (CSP frame-ancestors).
  • Every message between your page and the frames is origin-checked and versioned.
  • Session tokens never reach your page: no localStorage, sessionStorage or cookies on your site.

See security for the Content-Security-Policy your own page needs.