How to add validation errors manually?
Describe the bug and the expected behavior
I am biting my teeth out with this.
Looking for a way to add validation errors manually like fields.email.errors.push("Invalid email")
Also tried submission.reply in onSubmit, to no effect:
const [form, fields] = useForm<CreateUserInput>({
shouldValidate: "onSubmit",
shouldRevalidate: "onInput",
onValidate: ({ formData }) => parseWithZod(formData, { schema: createUserSchema }),
onSubmit: (e, { submission }) => {
e.preventDefault();
if (submission?.status !== "success") return;
try {
await createUser(submission.value);
} catch (e) {
return { ...submission, errors: { email: ["Invalid email"] } }
},
});
I am not using server actions.
Conform version
1.1.5
Steps to Reproduce the Bug or Issue
const [form, fields] = useForm<CreateUserInput>({
shouldValidate: "onSubmit",
shouldRevalidate: "onInput",
onValidate: ({ formData }) => parseWithZod(formData, { schema: createUserSchema }),
onSubmit: (e, { submission }) => {
e.preventDefault();
if (submission?.status !== "success") return;
try {
await createUser(submission.value);
} catch (e) {
return { ...submission, errors: { email: ["Invalid email"] } }
},
});
What browsers are you seeing the problem on?
No response
Screenshots or Videos
No response
Additional context
No response
@stabildev ¿are you using nextjs?
@stabildev ¿are you using nextjs?
Yes
The way you pass errors from the backend to the form, is trough lastResult property, which you're not using.
Try something like:
action.ts file
"use server";
import { parseWithZod } from "@conform-to/zod";
import { verifyOtpSchema } from "./schema";
export async function myServerAction(_: unknown, formData: FormData) {
const submission = parseWithZod(formData, {
schema: verifyOtpSchema,
});
if (submission.status !== "success") {
return submission.reply();
}
return submission.reply({
fieldErrors: {
name: ["my custom error"],
},
});
}
IMPORTANT!
Don't call the server action in the onSubmit function manually, instead use useActionState and pass the action property to the form. It's the most ergonomic way, and handles lastResult for you
form.tsx
"use client";
import { getFormProps, getInputProps, useForm } from "@conform-to/react";
import { parseWithZod } from "@conform-to/zod";
import { useActionState } from "react";
import { verify } from "./action";
import { mySchema } from "./schema";
export function MyForm() {
const [lastResult, action] = useActionState(myServerAction, undefined);
const [form, fields] = useForm({
lastResult,
onValidate({ formData }) {
return parseWithZod(formData, {
schema: mySchema,
});
},
});
return (
<form {...getFormProps(form)} action={action}>
<div>
<input {...getInputProps(fields.name, { type: "text" })} />
{fields.name.errors && <p>{fields.name.errors}</p>}
</div>
<button type="submit">Submit</button>
</form>
);
}
As I said, I am not using server actions (at all)
const [lastResult, setLastResult] = useState();
const [form, fields] = useForm<CreateUserInput>({
lastResult,
shouldValidate: "onSubmit",
shouldRevalidate: "onInput",
onValidate: ({ formData }) => parseWithZod(formData, { schema: createUserSchema }),
onSubmit: (e, { submission }) => {
e.preventDefault();
if (submission?.status !== "success") return;
try {
await createUser(submission.value);
} catch (e) {
setLastResult(submission.reply({ fieldsErrors: { email: ["Invalid email"] } }));
},
});
Right :)