# Next.js forms

A copy-paste Next.js form component that submits to Nisuform from the App Router or the Pages Router, without an API route or server action.

Collect the fields, `POST` them to your endpoint and check the response. You don't need an API route, a server action or environment variables, because the form key is safe to publish.

<Callout title="Generated for your form">
  The form's **Setup** tab, under **Code**, has this component ready with your endpoint and field names filled in. **Your design** gives you the styled form from the builder as a component. **Your own styles** gives you a bare component to style your way.
</Callout>

## The component

Forms need state and event handlers, so mark the component as a client component. It works in both the App Router and the Pages Router.

```jsx title="app/contact/ContactForm.jsx"
'use client'

import { useRef, useState } from 'react'

export function ContactForm() {
  const shownAt = useRef(Date.now())
  const [status, setStatus] = useState('idle')

  async function onSubmit(event) {
    event.preventDefault()
    setStatus('sending')
    const data = new FormData(event.currentTarget)
    data.set('_elapsed', String(Date.now() - shownAt.current))
    const response = await fetch('https://api.nisuform.com/s/YOUR_FORM_KEY', {
      method: 'POST',
      headers: { Accept: 'application/json' },
      body: data,
    })
    setStatus(response.ok ? 'sent' : 'error')
  }

  if (status === 'sent') {
    return <p>Thanks! We got your message.</p>
  }

  return (
    <form onSubmit={onSubmit}>
      <input type="email" name="email" placeholder="Email" required />
      <textarea name="message" rows={5} required />
      <input type="text" name="_gotcha" tabIndex={-1} autoComplete="off" style={{ display: 'none' }} />
      <button type="submit" disabled={status === 'sending'}>Send</button>
      {status === 'error' && <p role="alert">Something went wrong. Please try again.</p>}
    </form>
  )
}
```

## Use it on a page

Render it from any page. The page itself can stay a server component.

```jsx title="app/contact/page.jsx"
import { ContactForm } from './ContactForm'

export default function ContactPage() {
  return <ContactForm />
}
```

## What it does

* Sends `Accept: application/json`, so Nisuform answers with JSON instead of the thank-you page. See [JavaScript and fetch](/docs/connect/javascript).
* Adds `_elapsed`, the time the form was open, and keeps the hidden `_gotcha` honeypot empty. Both feed [spam protection](/docs/protection/spam). See [Special fields](/docs/connect/special-fields).
* Shows a success message when the submission is accepted, and an error when it isn't.

When a server action is worth it, and a version that works with `output: 'export'`, are in the [Next.js contact form guide](/blog/nextjs-contact-form).
