React patterns: Scroll to first validation error
25 August 2022 (Updated 4 September 2022)
When a form validation error occurs, you typically want to scroll the screen to the first error. This is especially important on long forms where after clicking a submit button, the user needs to be taken to the first error.
Here’s a very rough & incomplete outline of an approach you can take with Formik though it should work even if you aren’t using Formik.
1. Create <ScrollToFirstError>
component
Create a ScrollToFirstError
component that looks something like this:
import React, { useEffect } from 'react'
import { useFormikContext } from 'formik'
const ScrollToFirstError: React.FC = () => {
const { submitCount, isValid } = useFormikContext()
useEffect(() => {
// Wrap the code in setTimeout to make sure it runs after the DOM has been
// updated and has the error message elements.
// Can maybe use useLayoutEffect instead.
setTimeout(() => {
// Only run on submit
if (submitCount === 0) {
return
}
// Find the first error message
const errorMessageSelector = '[data-field-error]'
const firstErrorMessage = document.querySelector(errorMessageSelector)
if (!firstErrorMessage) {
console.warn(
`Form failed validation but no error field was found with selector: ${errorMessageSelector}`
)
return
}
// Find the first label with an error
const inputId = firstErrorMessage.getAttribute('data-field-error')
const errorLabelSelector = `label[for="${inputId}"]`
const firstErrorLabel = document.querySelector(errorLabelSelector)
if (!firstErrorLabel) {
console.warn(
`Could not find an error label with selector: ${errorLabelSelector}`
)
return
}
firstErrorLabel.scrollIntoView()
}, 100)
}, [submitCount, isValid])
return null
}
export default ScrollToFirstError
2. Use component inside Formik
import { Form, Formik } from 'formik'
const MyForm: React.FC = () => {
return (
<Formik>
<Form>
<ScrollToFirstError />
// ....
</Form>
</Formik>
)
}
Or create a <FormikEffect>
component that takes an onError
callback:
import { Form, Formik } from 'formik'
import { scrollToFirstError } from '../lib/utils'
const MyForm: React.FC = () => {
return (
<Formik>
<Form>
<FormikEffect
onError={scrollToFirstError}
/>
// ....
</Form>
</Formik>
)
}
TODO
- It’d be better to only scroll to the first error if the first error is not visible in the viewport. Otherwise, the screen jumps a bit and UX becomes a bit jarring.
- It’s probably better to create a wrapper component (e.g.,
<EnhancedFormik>
) that takes aonError
callback. That way, we don’t have to create a component just for a side effect – which feels a bit iffy.
Tagged:
React patterns
Thanks for your comment 🙏. Once it's approved, it will appear here.
Leave a comment