Stop Rewriting This React Form UX Logic

Published: (March 19, 2026 at 12:19 PM EDT)
3 min read
Source: Dev.to

Source: Dev.to

Cover image for Stop Rewriting This React Form UX Logic

Sayan Paul

The Hidden Problem

Libraries like React Hook Form and Formik are excellent at what they do. They manage form state, track dirty fields, and run validation. But they intentionally stop short of one thing: what happens in the DOM after validation fails.

That responsibility falls on you every single time.

It usually starts small:

const firstError = document.querySelector("[aria-invalid='true']");
firstError?.focus();

Then you need scrolling. Then you need to handle nested field paths. Then someone asks for an accessible error summary at the top of the form.

Before long, you’ve got a custom utility file full of DOM‑querying logic, tightly coupled to one project and never reused.

A Small Reusable Solution

I ran into this pattern enough times that I extracted it into a tiny open‑source package called react-form-ux.

The idea is simple: provide a few focused UX primitives for forms, and nothing else.

Current features

  • focusFirstError() – focuses the first invalid input in the DOM
  • scrollToError() – smooth‑scrolls to the first validation error
  • getErrorFields() – returns an array of field names that have errors (supports nested paths)
  • ErrorSummary – renders an accessible, clickable list of errors

The library is headless. It doesn’t ship any CSS or UI opinions. It doesn’t care whether you’re using React Hook Form, Formik, or a completely custom setup.

You pass it an errors object, and it handles the DOM behavior.

At a glance, the library helps you:

  • guide users to the first validation error
  • focus the correct input automatically
  • scroll long forms to the relevant field
  • render accessible error summaries

Demo

Submitting an invalid form automatically scrolls to the first error and focuses the field.

react-form-ux demo

Code Example

Here’s how it looks with React Hook Form and Zod:

import { useForm } from "react-hook-form";
import { useFormUX, ErrorSummary } from "react-form-ux";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";

const schema = z.object({
  email: z.string().email("Invalid email address"),
  password: z.string().min(8, "Must be at least 8 characters"),
});

export function SignupForm() {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm({
    resolver: zodResolver(schema),
  });

  const { focusFirstError, scrollToError } = useFormUX({ errors });

  const onSubmit = (data: z.infer<typeof schema>) => console.log(data);

  const onError = () => {
    setTimeout(() => {
      scrollToError();
      focusFirstError();
    }, 100);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit, onError)}>
      <div>
        <label htmlFor="email">Email</label>
        <input id="email" {...register("email")} />
        {errors.email && <p>{errors.email.message}</p>}
      </div>

      <div>
        <label htmlFor="password">Password</label>
        <input id="password" type="password" {...register("password")} />
        {errors.password && <p>{errors.password.message}</p>}
      </div>

      <ErrorSummary errors={errors} />

      <button type="submit">Sign Up</button>
    </form>
  );
}

The setTimeout gives React a moment to paint the error elements before the scroll and focus kick in. That’s the only gotcha.

Why This Exists

This package is not trying to replace your form library. The responsibilities are separate:

  • Form libraries handle validation, state, and data.
  • react-form-ux handles the UX behavior after validation fails.

By splitting these concerns, your form components stay clean and your users actually see what went wrong.

Installation

npm install react-form-ux

📦 npm package
🔗 GitHub repository

Early Stage — Feedback Welcome

This project is still evolving. The current feature set is intentionally small, but there’s room to grow.

If you’ve been writing this kind of form UX logic yourself, give the package a try. I’d genuinely appreciate:

  • Feedback on the API design
  • Suggestions for new features

Bug reports via GitHub Issues.

Written by Sayan Paul.

0 views
Back to Blog

Related posts

Read more »