Conform handles async validation (like checking if an email is unique) by falling back to server validation when client-side validation is insufficient.
- On the Client: Use a schema creator that returns a schema without the async implementation. In the
onValidate callback, if the async function is missing, use ctx.addIssue with message: conformZodMessage.VALIDATION_UNDEFINED and fatal: true. This signals Conform to trigger a server-side validation. - On the Server: Use
parseWithZod with the async: true option and provide the actual implementation of the async logic within the schema.
import { refine } from '@conform-to/zod';
function createSchema(
options?: { isEmailUnique: (email: string) => Promise<boolean>; },
) {
return z.object({
email: z.string().email().pipe(
z.string().superRefine((email, ctx) => {
if (typeof options?.isEmailUnique !== 'function') {
ctx.addIssue({
code: 'custom',
message: conformZodMessage.VALIDATION_UNDEFINED,
fatal: true,
});
return;
}
return options.isEmailUnique(email).then((isUnique) => {
if (!isUnique) {
ctx.addIssue({
code: 'custom',
message: 'Email is already used',
});
}
});
}),
),
});
}
export function action() {
const formData = await request.formData();
const submission = await parseWithZod(formData, {
schema: createSchema({
async isEmailUnique(email) { /* ... */ },
}),
async: true,
});
}
export default function Signup() {
const lastResult = useActionData();
const [form] = useForm({
lastResult,
onValidate({ formData }) {
return parseWithZod(formData, {
schema: createSchema(),
});
},
});
}