Validate on the server, always
Client validation is a UX nicety. Server validation is the security boundary. Anyone can POST to the action endpoint; you can never trust the client to have done the check.
Zod fits cleanly
Define a schema, call safeParse on the form input, branch on success/failure. On failure, return field-level errors via useActionState; on success, run the mutation and revalidate.
Type the state shape
Make the action's State type explicit. Front-end and back-end agree on the shape via TypeScript.