Capítulo 91 de 108

Next.js

Core Idea

Build accessible forms in Next.js with <Field /> components, useActionState and Server Actions — client and server-side validation without a form library.

Key Concepts

  • useActionState: React hook returning [formState, formAction, pending]; drives form state, submission and pending status.
  • <Form action={formAction}>: Next.js <Form /> from next/form enables navigation + progressive enhancement.
  • Server Action: "use server" function that receives (prevState, formData), validates with Zod safeParse(), returns { values, errors, success }.
  • data-invalid / aria-invalid: Set on <Field> and input respectively from !!formState.errors?.field?.length to trigger error styling and a11y.
  • data-disabled: Set on <Field> from pending to style disabled state during submission.
  • Reset on success: Omit values from the server action's success return; React resets the form to initial state automatically.
  • Preserve on error: Return values alongside errors so user input isn't lost on validation failure.

Code Examples

"use server"

export async function bugReportFormAction(
  _prevState: FormState,
  formData: FormData
) {
  const values = {
    title: formData.get("title") as string,
    description: formData.get("description") as string,
  }
  const result = formSchema.safeParse(values)
  if (!result.success) {
    return { values, success: false, errors: result.error.flatten().fieldErrors }
  }
  return { errors: null, success: true }
}
  • O que demonstra: padrão canônico de server action com validação Zod (safeParse), retorno de values no erro (preserva input) e omissão no sucesso (reseta form).
<Field data-invalid={!!formState.errors?.email?.length}>
  <FieldLabel htmlFor="email">Email</FieldLabel>
  <Input id="email" name="email" type="email" aria-invalid={!!formState.errors?.email?.length} />
  {formState.errors?.email && <FieldError>{formState.errors.email[0]}</FieldError>}
</Field>
  • O que demonstra: exibição de erro por campo com <FieldError />, sincronizando data-invalid (estilo) e aria-invalid (a11y).

Anti-patterns

  • Não retornar values no erro: o form reseta e o usuário perde o que digitou; sempre retorne values junto de errors.
  • Desabilitar validação nativa do browser sem compensar: os exemplos desativam validação nativa de propósito para exercitar a validação server-side — em produção, garanta que a validação Zod cobre os mesmos casos.

Key Takeaways

  1. useActionState + Server Actions substitui react-hook-form para casos simples/médios em Next.js, mantendo tudo server-first.
  2. Schema e tipo FormState devem viver em arquivo separado (schema.ts) para serem importados tanto no client quanto no server action.
  3. pending do useActionState cobre loading (Spinner) e disabled de inputs/botão simultaneamente.
  4. Formulários complexos (RadioGroup, Select, Checkbox group, Switch) seguem o mesmo padrão de data-invalid/aria-invalid por <FieldSet>/<Field>.

Connects To

  • Field component (ch da suíte forms): <Field>, <FieldGroup>, <FieldSet>, <FieldLegend> são a base de markup usada aqui.
  • react-19: useActionState é API do React 19, exige Next.js 15+/React 19.