Skip to content

@nexafon/vue is a Vue plugin with components and composables around the web SDK. Supported: Vue 3.5.

Terminal window
npm install @nexafon/vue

createNexafon(options) takes the Nexafon.init options and starts Nexafon once; it stops when the app unmounts.

examples/vue/src/main.ts
import { createNexafon } from '@nexafon/vue'
import { createApp } from 'vue'
import App from './App.vue'
const getUserToken = async () => {
const r = await fetch('/api/nexafon-token', { method: 'POST' })
if (!r.ok) throw new Error(`The token endpoint answered ${r.status}`)
return r.text()
}
createApp(App)
.use(
createNexafon({ publishableKey: import.meta.env.VITE_NEXAFON_PUBLISHABLE_KEY, getUserToken }),
)
.mount('#app')
examples/vue/src/App.vue
<script setup lang="ts">
import { CallButton, Inbox, Softphone } from '@nexafon/vue'
</script>
<template>
<main class="support">
<Inbox style="height: 640px" />
<aside>
<CallButton phone="+252615550142" />
<Softphone />
</aside>
</main>
</template>
<style>
.support { display: grid; gap: 16px; padding: 16px; grid-template-columns: 1fr 340px; }
</style>
Component Renders Props
<NexafonApp> <nexafon-app> store?
<Inbox> <nexafon-inbox> store?
<Softphone> <nexafon-softphone> store?
<CallButton> <nexafon-call-button> phone, store?

class and style fall through to the element. useNexafonEvent(name, listener) listens for the component’s lifetime; useNexafon() is a ref to the started API (null until it has started).

Writing the raw <nexafon-*> tags in your own templates needs compilerOptions.isCustomElement: (tag) => tag.startsWith('nexafon-'); the components above do not.

getUserToken calls your server, which signs a token with a server helper. The example’s POST /api/nexafon-token handler:

examples/vue/server/token.ts
import type { IncomingMessage, ServerResponse } from 'node:http'
import { createUserToken } from '@nexafon/node'
interface SignedInUser {
id: string
name: string
role: string
email: string
emailVerified: boolean
isStaff: boolean
}
// Your app's own session decides who is signed in, and its own records whether it has confirmed their email. The
// example has no sign-in: EXAMPLE_DEMO_USER_EMAIL is a demo user for local runs only, so replace this with your session.
function signedInUser(_req: IncomingMessage): SignedInUser | null {
const email = process.env.EXAMPLE_DEMO_USER_EMAIL
if (!email) return null
return {
id: 'demo-user-1',
name: 'Demo User',
role: process.env.EXAMPLE_DEMO_USER_ROLE || 'agent',
email,
// The demo address is a member you invited, so you have confirmed it.
emailVerified: true,
isStaff: true,
}
}
// Everyone who gets a token may reach your workspace's inbox and calls: sign only for your staff, never for customers
// who signed up. The example's only user is the demo teammate.
function mayUseNexafon(user: SignedInUser): boolean {
return user.isStaff
}
// POST /api/nexafon-token signs a token for the signed-in user with NEXAFON_SECRET_KEY.
export function userToken(req: IncomingMessage, res: ServerResponse): void {
if (req.method !== 'POST') {
res.writeHead(405).end()
return
}
const user = signedInUser(req)
if (!user) {
res.writeHead(401).end()
return
}
if (!mayUseNexafon(user)) {
res.writeHead(403).end()
return
}
const token = createUserToken({
userId: user.id,
name: user.name,
role: user.role,
email: user.email,
// Nexafon links a first sign-in to the member with this email only when it is verified: never send true for an
// address your app has not confirmed, or whoever sets a member's email on your app acts as that member.
emailVerified: user.emailVerified,
})
res.writeHead(200, { 'content-type': 'text/plain', 'cache-control': 'no-store' }).end(token)
}

signedInUser stands for your app’s own session: answer 401 when nobody is signed in. The example has no sign-in, so on a local run it signs for the demo member named in EXAMPLE_DEMO_USER_EMAIL and answers 401 without one; replace it with your session. mayUseNexafon answers 403 to signed-in users who must not use Nexafon: sign only for your staff, never for customers who signed up, since a token can make its user a member of your workspace. The example treats its demo user as staff.

The example mounts it as middleware of the Vite dev and preview servers in examples/vue/vite.config.ts; in production it is a route of your own server, behind your own session check.

The full example is examples/vue, tested with Playwright.