Skip to content

React Forms

@snipform/react runs a SnipForm form entirely from React state. Your markup, your styling, SnipForm’s backend: validation, spam protection, double-submit protection and Signals tracking all happen on the server. Nothing in the package touches the DOM.

Terminal window
npm install @snipform/react

Requires React 18 or newer.

ContactForm.tsx
import { useSnipForm, SnipForm, FieldError } from '@snipform/react';
export function ContactForm() {
const form = useSnipForm({
key: 'YOUR_FORM_KEY',
fields: {
name: { type: 'text', rules: { required: 'Tell us your name' } },
email: { type: 'email', rules: { required: 'Email is required', email: null } },
message: { type: 'textarea', rules: { required: null, 'max_length[2000]': null } },
},
});
if (form.fatal) return <p>{form.fatal}</p>;
if (form.success) return <div dangerouslySetInnerHTML={{ __html: form.success.html }} />;
return (
<SnipForm form={form}>
<input {...form.register('name')} placeholder="Name" />
<FieldError form={form} name="name" />
<input {...form.register('email')} placeholder="Email" />
<FieldError form={form} name="email" />
<textarea {...form.register('message')} />
<FieldError form={form} name="message" />
<button disabled={form.isSubmitting}>Send</button>
</SnipForm>
);
}

That is the whole thing. The form key comes from the form’s page in the dashboard. Each piece:

PieceWhat it does
useSnipForm(options)Owns values, errors, status and the server session. Reference
fieldsEvery field the form will submit, with its type and rules. The server validates exactly this set.
<SnipForm form={form}>A <form> with the hook’s handlers applied and the honeypot rendered.
form.register('name')Props for one input: value, onChange, onFocus, onBlur, onKeyDown, aria-invalid. Inputs
<FieldError form={form} name="name" />The first server message for a field, with role="alert", or nothing.
form.fatal / form.successThe two terminal states. States

The HTML library scans the DOM for sf-validate: directives and mutates elements directly: class names, text, innerHTML. React owns those elements and overwrites the changes on its next render, so error states flicker or disappear. This package keeps everything in React state and renders nothing itself beyond the optional components above.

  1. Idle until a human shows up. No request leaves until the visitor focuses, clicks, types, touches, or scrolls the form into view. That first interaction opens a server session (initializing, then ready).

  2. The field set is sent and frozen. fields goes to the server at init. From then on the server validates exactly those fields and refuses anything else, so declare every field the form can ever submit, including ones you render conditionally.

  3. Validation is the server’s. The same rules the dashboard documents, run by the backend. Nothing is duplicated client-side, so client and server cannot disagree. Validation

  4. Submit posts the values, the empty honeypot and the behavioural signals the spam scorer reads. Success returns the form’s thank-you HTML with %field% variables filled in. A validation failure returns every message per field and keeps the session open for another try.

The session mechanics (signatures, the 600 second TTL, single use, IP pinning) are shared with the other clients and described in How Forms Work.

The hook uses state, effects and fetch, so the component must be a client component. It is safe to render on the server: nothing is requested until a browser interaction happens.

app/contact/ContactForm.tsx
'use client';
import { useSnipForm, SnipForm, FieldError } from '@snipform/react';
export function ContactForm() {
const form = useSnipForm({ key: 'YOUR_FORM_KEY', fields: { email: { type: 'email', rules: { required: null, email: null } } } });
// ...
}

The server checks the request’s Referer against the property’s domain. For a localhost or 127.0.0.1 dev server, switch on the form’s localhost toggle in the dashboard. Other hostnames (0.0.0.0, *.local, LAN IPs) are not covered; use localhost.

  • useSnipForm - every option, and everything on the handle.
  • Inputs - text, select, checkbox groups, radios and hidden fields.
  • Validation - rules, messages and validate-on-blur.
  • States and success - render per status, show the thank-you, handle errors.