Skip to content

Svelte and other frameworks

Svelte, Solid, Lit, Qwik and plain JavaScript need no wrapper: load and start Nexafon once in the browser, then write the <nexafon-*> elements in your markup.

Terminal window
npm install @nexafon/js
examples/svelte/src/App.svelte
<script lang="ts">
import { loadNexafon, type NexafonApi } from '@nexafon/js'
import { onMount } from 'svelte'
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()
}
// Svelte uses the Web Components directly: start Nexafon once in the browser, then write the elements.
onMount(() => {
let nexafon: NexafonApi | undefined
loadNexafon().then((api) => {
nexafon = api?.init({
publishableKey: import.meta.env.VITE_NEXAFON_PUBLISHABLE_KEY,
getUserToken,
})
})
return () => nexafon?.destroy()
})
</script>
<main class="support">
<nexafon-inbox style="height: 640px"></nexafon-inbox>
<aside>
<nexafon-call-button phone="+252615550142"></nexafon-call-button>
<nexafon-softphone></nexafon-softphone>
</aside>
</main>
<style>
.support { display: grid; gap: 16px; padding: 16px; grid-template-columns: 1fr 340px; }
</style>

loadNexafon() resolves to null on the server, so the same code is safe in SvelteKit, SolidStart or Astro server code. With TypeScript JSX (Solid), declare the nexafon-* tags in JSX.IntrinsicElements.

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

examples/svelte/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/svelte/vite.config.ts; in production it is a route of your own server, behind your own session check.

The full example is examples/svelte.