Server Actions (Next.js)
Submit gform-react forms with Next.js Server Actions using the native action prop - no client wiring.
gform-react supports native <form> submission, including Next.js Server Actions. You submit via
the action prop instead of onSubmit. gform still runs client validation first and blocks an
invalid submit; because each GInput sets the input's name to its formKey, the action reads
values from FormData by formKey.
The action
A standard Server Action - signature (prevState, formData) => newState. Read fields by their
formKey.
"use server";
export const signIn = async (prevState: unknown, formData: FormData) => {
const email = formData.get("email")?.toString(); // 'email' = the GInput formKey
// …authenticate…
return { success: true };
};The form
Pass action={formAction} from useActionState. Do not use onSubmit and do not call
e.preventDefault() - that would cancel the native submission the action relies on.
"use client";
import { GForm, GInput, GValidator, type GValidators } from "gform-react";
import { useActionState, useEffect } from "react";
import { signIn } from "@/app/actions/sign-in";
const validators: GValidators = {
email: new GValidator()
.withRequiredMessage("Email is required")
.withTypeMismatchMessage("Enter a valid email"),
};
const initial = {};
export function EmailForm({ onSuccess }: { onSuccess: () => void }) {
const [state, formAction, pending] = useActionState(signIn, initial);
useEffect(() => {
if (state.success) onSuccess();
}, [state, onSuccess]);
return (
<GForm action={formAction} validators={validators}>
{/* action, NOT onSubmit */}
<GInput
formKey="email"
required
type="email"
element={(input, props) => (
<div>
<input {...props} />
{input.error && <small>{input.errorText}</small>}
</div>
)}
/>
<button disabled={pending}>{pending ? "Sending…" : "Send code"}</button>
</GForm>
);
}The contract
- Use
action={formAction}- notonSubmit, and do not calle.preventDefault(). - gform validates on the client first; an invalid form never reaches the server action.
- The action signature is
(prevState, formData) => newState; read fields byformKey. - Each
GInput'snameequals itsformKey, soformData.get("<formKey>")returns the value.
gform deliberately never calls e.preventDefault() for you. Calling it yourself here would cancel
the native form submission that Server Actions depend on. Use action and let the browser submit.
Because submission is native, file inputs are included in the FormData the action receives - see
File Inputs.