> ## Documentation Index
> Fetch the complete documentation index at: https://developer.keverd.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Next.js

> App Router with @keverdjs/react and server verify

# Next.js

## Env

```bash theme={null}
NEXT_PUBLIC_KEVERD_PUBLIC_KEY=kv_pk_test_…
KEVERD_SECRET_KEY=kv_sk_test_…
```

```bash theme={null}
npm install @keverdjs/react @keverd/node
```

## Provider (App Router)

```tsx theme={null}
// app/components/KeverdWrapper.tsx
'use client';

import { KeverdProvider } from '@keverdjs/react';

export function KeverdWrapper({ children }: { children: React.ReactNode }) {
  return (
    <KeverdProvider apiKey={process.env.NEXT_PUBLIC_KEVERD_PUBLIC_KEY!}>
      {children}
    </KeverdProvider>
  );
}
```

Wrap children with `KeverdWrapper` in `app/layout.tsx`.

## Client collect

```tsx theme={null}
'use client';

import { useKeverdVisitorData } from '@keverdjs/react';

export default function LoginPage() {
  const { getData, isLoading } = useKeverdVisitorData({ immediate: false });

  async function login() {
    const visitor = await getData();
    await fetch('/api/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ eventId: visitor.requestId }),
    });
  }

  return (
    <button disabled={isLoading} onClick={() => void login()}>
      Continue
    </button>
  );
}
```

## Server verify

```ts theme={null}
// app/api/login/route.ts
import { Keverd } from '@keverd/node';
import { NextResponse } from 'next/server';

const keverd = new Keverd({ secretKey: process.env.KEVERD_SECRET_KEY! });

export async function POST(req: Request) {
  const { eventId } = await req.json();
  const decision = await keverd.verify(eventId);

  if (decision.action === 'block') {
    return NextResponse.json(
      { error: 'Blocked', reasons: decision.reasons },
      { status: 403 },
    );
  }

  return NextResponse.json({ ok: true, visitorId: decision.visitor_id });
}
```
