Menu

Next.js

App Router client component. Drop it at app/contact/page.tsx and the route is live.

One-liner

// app/contact/page.tsx — client component
'use client';

import { useState } from 'react';

const ENDPOINT = 'https://gopigeon.dev/f/f_abc123def456xyz0';
// One-time setup: curl -X POST https://gopigeon.dev/new -d 'recipient=you@example.com'

export default function Contact() {
  const [sent, setSent] = useState(false);
  async function handle(e: React.FormEvent<HTMLFormElement>) {
    e.preventDefault();
    const body = new URLSearchParams(new FormData(e.currentTarget)).toString();
    const r = await fetch(ENDPOINT, {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body,
    });
    if (r.ok) setSent(true);
  }
  if (sent) return <p>Thanks — we'll reply soon.</p>;
  return (
    <form onSubmit={handle}>
      <input name="name" required />
      <input name="email" type="email" required />
      <textarea name="message" required />
      <button type="submit">Send</button>
    </form>
  );
}

How it works

The 'use client' directive marks the file as a client component so useState and the onSubmit handler execute in the browser. The rest is identical to the React recipe: build a form-encoded body, fetch the endpoint, flip state on success.

If you prefer a server action over a client component, wire the same fetch call into an async server function, read formData from the action’s FormData argument, and let Next.js handle the progressive-enhancement fallback. gopigeon does not care which path the request comes from.

The email you passed as recipient above gets a one-click claim link on the first real submission — that is how you become the authenticated owner.

Not what you're looking for? See the full surface at /llms.txt.