Skip to content

Async validation fallback with superRefine not triggering server action #915

@armel-zeli-minlay

Description

@armel-zeli-minlay

Describe the bug and the expected behavior

Hi Conform team 👋

First of all, thank you for this fantastic library — we’ve been using it successfully with a multi-step form in React 19 and Next.js 15 (App Router). However, we’re struggling to get async validation via superRefine working live on blur, without waiting for a form submission.


✅ What we're trying to achieve

When the user types a laboratory name and blurs the input, we want to:

  • Trigger a request to the backend (via a server action) to check if the name already exists
  • Immediately display an error message if the name is already taken
  • Without submitting the form or advancing the step (this is part of a multi-step flow)

We followed the official Conform async validation guide, including the conformZodMessage.VALIDATION_UNDEFINED fallback mechanism.

⏪ Before following the doc, we also tried something more naive

name: z
  .string({
    required_error: "Le nom est obligatoire",
  })
  .min(3, "Le nom est trop court")
  .superRefine((name, ctx) => {
    if (typeof window !== "undefined") {
      fetch("/api/laboratories/check-name", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ name }),
      })
        .then((res) => res.json())
        .then((data) => {
          if (data.exists) {
            console.log("nom déjà utilisé !!!");
            ctx.addIssue({
              code: "custom",
              message: "Ce nom est déjà utilisé",
            });
          } else {
            console.log("nom disponible");
          }
        })
        .catch(() => {
          ctx.addIssue({
            code: "custom",
            message: "Erreur lors de la vérification du nom",
          });
        });
    }
  }),

The logs worked correctly, and the API was called — but Conform never displayed the error, which is expected since the validation was async in a synchronous context.


Conform version

v1.2.2

Steps to Reproduce the Bug or Issue


🧪 Here's what we've implemented

1. Zod schema with fallback superRefine

export function createInformationStepSchema(options?: {
  isLaboratoryNameTaken?: (name: string) => Promise<boolean>;
}) {
  return baseInformationStepSchema.extend({
    name: baseInformationStepSchema.shape.name.pipe(
      z.string().superRefine((name, ctx) => {
        if (!options?.isLaboratoryNameTaken) {
          ctx.addIssue({
            code: "custom",
            message: conformZodMessage.VALIDATION_UNDEFINED,
            fatal: true,
          });
          return;
        }

        return options.isLaboratoryNameTaken(name).then((taken) => {
          if (taken) {
            ctx.addIssue({
              code: "custom",
              message: "Ce nom de laboratoire est déjà utilisé",
            });
          }
        });
      }),
    ),
  });
}

2. Server action used by Conform

export async function isLaboratoryNameTaken(name: string): Promise<boolean> {
  const data = await execute(findLaboratoryByName, { name });
  const laboratories = data?.groupLaboratories || [];
  return laboratories.length > 0;
}
export async function validateInformationStep(_: unknown, formData: FormData) {
  console.log("⚠️ Server action triggered?");
  return parseWithZod(formData, {
    schema: createInformationStepSchema({ isLaboratoryNameTaken }),
    async: true,
  });
}

3. useForm and useActionState in create-form.tsx

const [lastResult, formAction] = useActionState(
  validateInformationStep,
  undefined,
);

const [form] = useForm<createLaboratorySchemaType>({
  id: formId,
  defaultValue: {
    [currentStep.key]: currentStepData,
  },
  lastResult,
  onValidate({ formData }) {
    return parseWithZod(formData, {
      schema: currentStep.schema,
    });
  },
  shouldValidate: "onBlur",
  shouldRevalidate: "onInput",
  // ⚠️ Adding `action: formAction` doesn't seem to change anything
});

🧨 The issue

  • The async validation doesn't work on blur.
  • The fallback server action (validateInformationStep) is never called.
  • Instead, the browser performs a full page reload as soon as we blur the input (after entering a valid-enough string).
  • No errors are shown and console.log in the server action never runs.

🧵 Summary

We are trying to perform a server-side uniqueness check via superRefine, triggered automatically on blur, using the documented VALIDATION_UNDEFINED fallback strategy — but it never works as expected, and the form reloads the page instead.

We're using:

  • React 19
  • Next.js 15 (App Router + Server Actions)
  • Conform 1.4
  • Zod

🙏 Question

Is there anything missing in our setup?

  • Is this feature (live async validation with fallback via server action) meant to work on blur?
  • If yes, could you clarify what's required to make it work properly?

Thanks so much for your help! 🙌

What browsers are you seeing the problem on?

No response

Screenshots or Videos

No response

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    v2 testedIssues addressed in the future v2 export

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions