# Next.js

> Use @nexafon/react in the Next.js app router with a route handler that signs user tokens.

Source: https://docs.nexafone.so/v1/web/nextjs/

`@nexafon/react` works in the app router: every export is marked `'use client'` and nothing touches `window` during
server rendering.

:::note[Not published yet]
The Nexafon packages are not on npm, Packagist or PyPI yet, and `go get` finds the Go module only once the Nexafon
repository is public and tagged, so installing them by name fails for now. Until then,
[install them from a checkout of the repo](/v1/quickstart/#install-from-a-checkout). The install commands shown here
are the ones to use after release.
:::

```sh
npm install @nexafon/react @nexafon/node
```

## 1. A route handler for tokens

The secret key stays on the server, in a route handler (`NEXAFON_SECRET_KEY` in the server’s environment, never a
`NEXT_PUBLIC_` variable):

```ts title="examples/next/app/api/nexafon-token/route.ts"
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: Request): 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
}

// Signs a token for the signed-in user with NEXAFON_SECRET_KEY.
export function POST(req: Request) {
  const user = signedInUser(req)
  if (!user) return new Response(null, { status: 401 })
  if (!mayUseNexafon(user)) return new Response(null, { status: 403 })
  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,
  })
  return new Response(token, {
    headers: { 'content-type': 'text/plain', 'cache-control': 'no-store' },
  })
}
```

`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.

:::caution[Send `emailVerified` as true only for a confirmed address]
Nexafon links a user’s first sign-in to the workspace member with the token’s email only when it is verified. If your
app lets users set an email it has not confirmed, someone could register with a member’s address and act as that
member. Send `true` only when your app has confirmed the address, from your own user record, never as a constant.
:::

## 2. The provider in a client component

`getUserToken` is a function, so render `<NexafonProvider>` from a client component (`'use client'`):

```tsx title="examples/next/app/support.tsx"
'use client'

import { CallButton, Inbox, NexafonProvider, Softphone } from '@nexafon/react'

// getUserToken is a function, so the provider is rendered from a client component.
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()
}

export function Support({ publishableKey }: { publishableKey: string }) {
  return (
    <NexafonProvider publishableKey={publishableKey} getUserToken={getUserToken}>
      <main style={{ display: 'grid', gap: 16, padding: 16, gridTemplateColumns: '1fr 340px' }}>
        <Inbox style={{ height: 640 }} />
        <aside>
          <CallButton phone="+252615550142" />
          <Softphone />
        </aside>
      </main>
    </NexafonProvider>
  )
}
```

## 3. The page

The page is a server component. It reads `NEXAFON_PUBLISHABLE_KEY` on each request (`dynamic = 'force-dynamic'`), so
one build serves any key, and passes it to the client component:

```tsx title="examples/next/app/page.tsx"
import { Support } from './support'

// Read on each request, so one build serves any key.
export const dynamic = 'force-dynamic'

export default function Page() {
  return <Support publishableKey={process.env.NEXAFON_PUBLISHABLE_KEY ?? ''} />
}
```

Use the [React components and hooks](/v1/web/react/) inside it as usual. The full example is `examples/next`.
