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

# Next.js

> Use FormBnB with Next.js applications

Next.js apps can submit directly from a Client Component, through a Server Action, or through an API route when you need custom validation.

## Client Component

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

import { useState } from 'react';

const endpoint = process.env.NEXT_PUBLIC_FORMBNB_ENDPOINT!;

export function ContactForm() {
  const [status, setStatus] = useState<'idle' | 'sending' | 'success' | 'error'>('idle');

  async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
    event.preventDefault();
    if (status === 'sending') return;

    setStatus('sending');
    const form = event.currentTarget;

    const response = await fetch(endpoint, {
      method: 'POST',
      body: new FormData(form),
      headers: { Accept: 'application/json' },
    });

    if (response.ok) {
      form.reset();
      setStatus('success');
    } else {
      setStatus('error');
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      <input name="email" type="email" required />
      <textarea name="message" required />
      <button type="submit" disabled={status === 'sending'}>
        {status === 'sending' ? 'Sending...' : 'Send'}
      </button>
      {status === 'success' && <p>Thanks. Your message was sent.</p>}
      {status === 'error' && <p>Something went wrong.</p>}
    </form>
  );
}
```

```env theme={null}
NEXT_PUBLIC_FORMBNB_ENDPOINT=https://api.formbnb.com/f/YOUR_ORG/YOUR_FORM
```

## Server Action

```tsx theme={null}
// app/contact/page.tsx
import { submitContact } from './actions';

export default function ContactPage() {
  return (
    <form action={submitContact}>
      <input name="email" type="email" required />
      <textarea name="message" required />
      <button type="submit">Send</button>
    </form>
  );
}
```

```tsx theme={null}
// app/contact/actions.ts
'use server';

import { redirect } from 'next/navigation';

export async function submitContact(formData: FormData) {
  const response = await fetch('https://api.formbnb.com/f/YOUR_ORG/YOUR_FORM', {
    method: 'POST',
    body: formData,
    headers: { Accept: 'application/json' },
  });

  if (!response.ok) {
    throw new Error('Form submission failed');
  }

  redirect('/thanks');
}
```

## API Route Proxy

```tsx theme={null}
// app/api/contact/route.ts
import { NextResponse } from 'next/server';

export async function POST(request: Request) {
  const formData = await request.formData();
  const email = formData.get('email');

  if (!email || !email.toString().includes('@')) {
    return NextResponse.json({ error: 'Invalid email' }, { status: 400 });
  }

  const response = await fetch('https://api.formbnb.com/f/YOUR_ORG/YOUR_FORM', {
    method: 'POST',
    body: formData,
    headers: { Accept: 'application/json' },
  });

  const data = await response.json().catch(() => ({}));
  return NextResponse.json(data, { status: response.status });
}
```
