View on GitHub

React Hook Form Plus

(RHF+)

ScrollIntoView method on field refs

Purpose

The scrollIntoView method enhancement exposes the native DOM scrollIntoView() function on field refs returned by useController and Controller components. This feature enables developers to programmatically scroll to form fields, which is particularly useful for error handling, form navigation, and improving user experience.

Benefits

API Changes

Property updates

export type ControllerRenderProps<
  TFieldValues extends FieldValues = FieldValues,
  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = {
  // ... existing properties
  ref: RefCallBack & {
    scrollIntoView?: (options?: ScrollIntoViewOptions) => void; // New method added
  };
};

Description

The scrollIntoView method enhancement exposes the native DOM scrollIntoView() function on field refs returned by useController and Controller components, enabling programmatic scrolling to form fields.

Behavior

Method signature:

field.ref.scrollIntoView(options?: ScrollIntoViewOptions) => void

Parameters:

Examples

Basic usage with useController

import { useController, useForm } from '@bombillazo/rhf-plus';

function MyForm() {
  const { control } = useForm();
  const { field, fieldState } = useController({
    name: 'email',
    control,
    rules: { required: 'Email is required' },
  });

  const handleScrollToField = () => {
    // Smooth scroll to the field
    field.ref.scrollIntoView({
      behavior: 'smooth',
      block: 'center',
    });
  };

  return (
    <div>
      <input {...field} placeholder="Email" />
      {fieldState.error && (
        <button onClick={handleScrollToField}>Scroll to Email Field</button>
      )}
    </div>
  );
}

Error handling workflow

import { useForm, useController } from '@bombillazo/rhf-plus';

function FormWithErrorHandling() {
  const { control, handleSubmit, formState } = useForm();

  const emailController = useController({
    name: 'email',
    control,
    rules: {
      required: 'Email is required',
      pattern: {
        value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
        message: 'Invalid email address',
      },
    },
  });

  const passwordController = useController({
    name: 'password',
    control,
    rules: { required: 'Password is required', minLength: 8 },
  });

  const onSubmit = (data) => {
    console.log(data);
  };

  const onError = (errors) => {
    // Scroll to first field with error
    const firstErrorField = Object.keys(errors)[0];

    if (
      firstErrorField === 'email' &&
      emailController.field.ref.scrollIntoView
    ) {
      emailController.field.ref.scrollIntoView({
        behavior: 'smooth',
        block: 'center',
      });
    } else if (
      firstErrorField === 'password' &&
      passwordController.field.ref.scrollIntoView
    ) {
      passwordController.field.ref.scrollIntoView({
        behavior: 'smooth',
        block: 'center',
      });
    }
  };

  return (
    <form onSubmit={handleSubmit(onSubmit, onError)}>
      <div>
        <input {...emailController.field} placeholder="Email" />
        {emailController.fieldState.error && (
          <p style=>
            {emailController.fieldState.error.message}
          </p>
        )}
      </div>

      <div>
        <input
          {...passwordController.field}
          type="password"
          placeholder="Password"
        />
        {passwordController.fieldState.error && (
          <p style=>
            {passwordController.fieldState.error.message}
          </p>
        )}
      </div>

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

Usage with Controller component

import { Controller, useForm } from '@bombillazo/rhf-plus';

function MyForm() {
  const { control } = useForm();

  return (
    <form>
      <Controller
        name="description"
        control={control}
        rules=
        render={({ field, fieldState }) => (
          <div>
            <textarea {...field} placeholder="Description" />
            {fieldState.error && (
              <div>
                <p style=>{fieldState.error.message}</p>
                <button
                  type="button"
                  onClick={() =>
                    field.ref.scrollIntoView?.({
                      behavior: 'smooth',
                      block: 'center',
                    })
                  }
                >
                  Scroll to Description
                </button>
              </div>
            )}
          </div>
        )}
      />
    </form>
  );
}

Limitations

Backward compatibility

This enhancement is fully backward compatible: