Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(react-form): correctly handle client-side validation with NextJS server-action #1299

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

xStrixU
Copy link

@xStrixU xStrixU commented Mar 17, 2025

Currently, there is no good, standardized way to properly handle client-side validation along with NextJS server-actions. The TanStack form supports server-actions, but the current implementation has one huge drawback: the server-action calls even if the client-side validation fails.

Client-side validation when submitting a form to a server-action is very important, especially for the user experience. The user sees validation errors immediately, instead of waiting for a response from the server. In addition, there is no reason to call a server-action if the client-side validation fails. The server will perform the same validation that takes place on the client side to eventually fail and return an error, so what is the purpose of pointlessly burdening the server?

So the reason for this feature is to make client-side validation with server-actions finally pleasant.

To prevent the server-action from executing, event.preventDefault() must be called synchronously. The problem is that form validation can also happen asynchronously, so there is no easy way to handle this. So event.preventDefault() calls immediately, then form validation occurs, and if successful, event.target.requestSubmit() is called to call onSubmit again. In the next iteration of onSubmit the code knows that it has already been called and the validation has passed, so it does not call event.preventDefault() and the server-action can be executed.

Some people have tried to implement this simply by calling event.target.submit() after successful validation, but with this approach the page is refreshed, so it completely fails with NextJS. I also noticed that calling onSubmit again doesn't work without this line: await new Promise((resolve) => setTimeout(resolve, 0)), so it's just a weird trick to make it work.

Stopping the execution of the server-action must be done in onSubmit, because then we do not lose the progressive enhancement: if the form data is invalid and JavaScript is enabled, then validation is done on the client side, and the server-action call is blocked. Otherwise, onSubmit cannot be called by the lack of JavaScript, so the server-action is called immediately, and all validation is done on the server, so everything still works correctly.

@xStrixU
Copy link
Author

xStrixU commented Mar 20, 2025

I'm also wondering if it's a good idea to mutate the useForm API, since this assigns the NextJS-based handleActionSubmit implementation to every other framework. It will not be possible to modify this code for any other specific framework, so this code should lie and be accessible only through @tanstack/react-form/nextjs. Maybe this approach is better?:

import { useForm } from '@tanstack/react-form';
import { useActionSubmit } from '@tanstack/react-form/nextjs';

import { action } from './actions';

export const ExampleForm = () => {
  const form = useForm({ ... });
  const handleActionSubmit = useActionSubmit(form);

  return (
    <form action={action} onSubmit={handleActionSubmit}>
      ...
    </form>
  );
};

Copy link

nx-cloud bot commented Mar 28, 2025

View your CI Pipeline Execution ↗ for commit 5f2678b.

Command Status Duration Result
nx affected --targets=test:sherif,test:knip,tes... ❌ Failed 1m 23s View ↗
nx run-many --target=build --exclude=examples/** ✅ Succeeded 8s View ↗

☁️ Nx Cloud last updated this comment at 2025-03-28 10:59:04 UTC

Copy link

pkg-pr-new bot commented Mar 28, 2025

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant