---
name: forminit-nextjs
description: Integrates Forminit headless form backend into Next.js applications using the npm SDK with server-side API key proxy. Creates an API route with createForminitProxy and a client form component with typed blocks (sender, text, email, phone, file, rating, date, select, radio, checkbox, country, number). Use when adding forms to Next.js apps, setting up form submission with server-side proxy, or collecting data in React/Next.js projects. Do not use for HTML/static sites (use forminit-html) or Nuxt.js (use forminit-nuxtjs).
metadata:
  author: forminit
  version: "1.0"
---

# Forminit — Next.js Integration

> Forminit is a headless form backend API. You build the form UI, Forminit handles submission, validation, storage, and notifications. This skill uses the npm SDK with a server-side proxy to keep API keys secure.

## Constraints

- Never expose API keys in client-side code. API keys must only be used on the server.
- User must set authentication mode to "Protected" in Form settings before creating an API token.
- User must add their API token to `.env.local` — remind them if they haven't.
- All field names use prefix pattern: `fi-{type}-{name}`
- Sender block: max 1 per form. Use for submitter's contact info.
- Default to `fi-sender-firstName` + `fi-sender-lastName`. Use `fi-sender-fullName` only if user wants a single name field.
- For non-submitter emails/phones/countries, use field blocks: `fi-email-invitee`, `fi-phone-emergency`, `fi-country-shipping`
- File uploads require FormData (not JSON)
- Max 50 blocks per form, max 25 MB total files
- Show success message on page after submit (no redirect unless requested)

## Install

```bash
npm install forminit@latest
```

## Environment Variable

```
# .env.local
FORMINIT_API_KEY="YOUR-SECRET-API-TOKEN"
```

## API Route

```ts
// app/api/forminit/route.ts
import { createForminitProxy } from 'forminit/next';

const forminit = createForminitProxy({
  apiKey: process.env.FORMINIT_API_KEY,
});

export const POST = forminit.POST;
```

## Form Component (FormData)

```tsx
'use client';

import { useState } from 'react';
import { Forminit } from 'forminit';

export function ContactForm() {
  const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
  const [error, setError] = useState<string | null>(null);
  const forminit = new Forminit({ proxyUrl: '/api/forminit' });

  async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
    e.preventDefault();
    setStatus('loading');
    setError(null);

    const form = e.currentTarget;
    const formData = new FormData(form);
    const { data, redirectUrl, error } = await forminit.submit('FORM_ID', formData);

    if (error) {
      setStatus('error');
      setError(error.message);
      return;
    }

    setStatus('success');
    form.reset();
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" name="fi-sender-firstName" placeholder="First name" required />
      <input type="text" name="fi-sender-lastName" placeholder="Last name" required />
      <input type="email" name="fi-sender-email" placeholder="Email" required />
      <textarea name="fi-text-message" placeholder="Message" required />

      {status === 'error' && <p>{error}</p>}
      {status === 'success' && <p>Message sent!</p>}

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

Replace `FORM_ID` with the form ID from https://app.forminit.com.

## Form Blocks Reference

### Sender Block — `fi-sender-{property}`

One per form. Collects submitter info. At least one property required.

```
fi-sender-email        fi-sender-firstName    fi-sender-lastName     fi-sender-fullName
fi-sender-phone        fi-sender-company      fi-sender-position     fi-sender-title
fi-sender-userId       fi-sender-address      fi-sender-city         fi-sender-country
```

### Field Blocks — `fi-{type}-{name}`

Each name must be unique. Up to 50 total blocks.

```
text      fi-text-{name}       e.g. fi-text-message
number    fi-number-{name}     e.g. fi-number-quantity
email     fi-email-{name}      e.g. fi-email-invitee
phone     fi-phone-{name}      e.g. fi-phone-emergency          Validation: E.164 (+12025550123)
url       fi-url-{name}        e.g. fi-url-website
date      fi-date-{name}       e.g. fi-date-appointment         Validation: ISO 8601 (YYYY-MM-DD)
rating    fi-rating-{name}     e.g. fi-rating-satisfaction      Validation: integer 1–5
select    fi-select-{name}     e.g. fi-select-plan              Supports multi-select (string[])
radio     fi-radio-{name}      e.g. fi-radio-priority           Single choice (string)
checkbox  fi-checkbox-{name}   e.g. fi-checkbox-features        Supports multi-choice (string[])
file      fi-file-{name}       e.g. fi-file-resume              Add [] for multiple: fi-file-photos[]
country   fi-country-{name}    e.g. fi-country-shipping         Validation: ISO 3166-1 alpha-2
```

## JSON Submission (alternative to FormData)

Use when building payloads programmatically. Cannot be used with file uploads.

```ts
const { data, redirectUrl, error } = await forminit.submit('FORM_ID', {
  blocks: [
    { type: 'sender', properties: { email: '...', firstName: '...', lastName: '...' } },
    { type: 'text', name: 'message', value: '...' },
    { type: 'rating', name: 'satisfaction', value: 4 },
    { type: 'select', name: 'plan', value: 'pro' }
  ]
});
```

Sender uses `{ type, properties }`. All other blocks use `{ type, name, value }`.

## SDK Response

```ts
const { data, redirectUrl, error } = await forminit.submit(formId, payload);
```

Success: `data.hashId` (submission ID), `data.date`, `data.blocks` (submitted values), `redirectUrl` (thank-you page URL).

Error: `error.error` (code like `FI_SCHEMA_FORMAT_EMAIL`), `error.code` (HTTP status), `error.message` (human-readable).

## Common Mistakes

- Using `name="email"` instead of `name="fi-sender-email"` — all fields must use the `fi-` prefix or the submission will be empty.
- Using `name="fi-sender-name"` — there is no `name` property on sender. Use `fi-sender-firstName` + `fi-sender-lastName` or `fi-sender-fullName`.
- Putting the API key in client-side code or `NEXT_PUBLIC_` env vars — the API key must only exist in the server-side route file via `process.env.FORMINIT_API_KEY`.
- Forgetting to create the API route at `app/api/forminit/route.ts` — without it, the proxy URL `/api/forminit` returns 404.
- Setting form authentication to "Public" instead of "Protected" — Next.js integration requires Protected mode with an API token.
- Sending phone without E.164 format — `5551234567` will be rejected, must be `+15551234567`.
- Using JSON with file uploads — files only work with FormData.
- Missing `'use client'` directive — the form component must be a client component since it uses `useState` and event handlers.

## Spam Protection (only if user requests)

- reCAPTCHA: https://forminit.com/docs/recaptcha/
- hCaptcha: https://forminit.com/docs/hcaptcha/
