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

# Next.js

Build a basic contact form in Next.js that submits directly to PipedForm using `fetch` on the client side, with state for handling submission status.

## Basic Example

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

import { useState } from 'react';

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

  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    setStatus('submitting');

    const formData = new FormData(e.currentTarget);

    try {
      const response = await fetch('https://pipedform.com/f/{form_id}', {
        method: 'POST',
        body: formData,
        headers: {
          'Content-Type': 'application/json',
        },
      });

      const data = await response.json();

      if (response.ok) {
        setStatus('success');
        setMessage('Form submitted successfully');
        e.currentTarget.reset();
      } else {
        setStatus('error');
        setMessage(data.message ?? 'Something went wrong. Please try again.');
      }
    } catch (error) {
      setStatus('error');
      setMessage('Network error. Please check your connection.');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input id="name" name="name" required />
      <input id="email" name="email" type="email" required />
      <textarea id="message" name="message" required></textarea>

      {status === 'success' && <p style={{ color: 'green' }}>{message}</p>}
      {status === 'error' && <p style={{ color: 'red' }}>{message}</p>}

      <button type="submit" disabled={status === 'submitting'}>
        {status === 'submitting' ? 'Sending...' : 'Submit'}
      </button>
    </form>
  );
}
```
