{
  "id": 17166835,
  "title": "The Save Button That Did Nothing",
  "url": "https://urgent.news/2026/10/30/the-save-button-that-did-nothing",
  "topic": "tech",
  "section": "Tech",
  "published": "2026-10-30T03:18:38.000Z",
  "source": {
    "name": "Dev.to",
    "slug": "dev-to",
    "url": "https://dev.to/grinushka/the-save-button-that-did-nothing-19i0"
  },
  "original_language": "en",
  "account": "Have you ever clicked Save on a long form and watched absolutely nothing happen? No toast. No red text. No movement. Just a button that seems to be ignoring you. That was the problem with our app. We build long forms such as profile pages, multi-section settings, and repeatable blocks like contacts or addresses. The issue was that validation errors were hidden at the bottom of the page, which users often couldn't see. The error occurred even though server-side validation was returning field errors, and even though the form submitted and the backend responded with 400 errors. We needed a consistent solution for every form: when validation fails, bring the first error into view.\n\nOur form relied on React Hook Form (RHF) for form state, Zod schemas through zodResolver, MUI inputs, and various composite controls such as autocompletes, date pickers, chip selectors, and custom list widgets. We also used React Router for navigation and had shared FormItem wrapper components that rendered the label, control, and error message for every field. RHF already had a feature called shouldFocusError, which focused the first invalid field registered with a focusable ref. However, this approach had limitations. Composite controls often didn't expose a focusable ref, or they exposed the wrong element. Array-level errors belonged to no input, so there was nothing to focus. Some fields were read-only displays of a value picked in a dialog, making focusing them pointless. Focus didn't always scroll to a comfortable position, and the field could land right under the sticky header.\n\nTo address this issue, we decided to target the error message element instead of focusing the input. The approach consisted of four steps: mark, search, scroll, and wait.\n\n1. Mark: We tagged every error message with its field path using the RHF field path attribute. The shared FormItem component rendered the error text, and we added the data-name attribute to it. This approach worked for any control type, including composites and custom widgets, and for errors with no input at all. For example, a list-level error could render its own message with data-name=\"contacts\".\n\n2. Search: We found the path of the first error by checking the shape of the form values. If the errors object had index keys, it was a field array error. We then determined the first index present and the first sub-field inside that item. If there were no index keys, we used the top-level name as the path. We used a regex to distinguish between plain FieldError and field array error shapes.\n\n3. Scroll: Once we had the path, we let the browser scroll the field into view using document.querySelector(). The scrollIntoView() method was used with smooth scrolling and center block alignment to ensure the label, control, and message were all visible. We also ensured that the field was focused, not just scrolled into view, and accounted for reduced motion preferences.\n\n4. Wait: Before scrolling, we waited for the page to settle. We implemented a setTimeout with a guess of one second, but it varied based on the user's device. Instead of guessing, we waited for the error message to settle by checking if the element existed and if it was in the same place as in the previous frame. This ensured a smooth user experience on various devices.",
  "summary": "Have you ever clicked Save on a long form and watched absolutely nothing happen? No toast. No red text. No movement. Just a button that seems to be ignoring you. That was our app. We build long forms: profile pages, multi-section settings, repeatable blocks like \"contacts\" or \"addresses\". Here's what kept happening: The user fills in the visible part of the form and clicks Save , which sits in a…",
  "key_points": [
    "Marked error messages with field path attribute",
    "Used RHF shouldFocusError, but had limitations",
    "Waited for page to settle before scrolling"
  ],
  "editors_take": "Prioritizing error message visibility over input focus solves the navigation failures caused by complex composite controls and hidden validation errors in long, multi-section web forms.",
  "illustration": null,
  "coverage": {
    "outlets": 1,
    "also_reported_by": []
  },
  "ai_generated": true,
  "disclaimer": "Summaries, key points and the editor’s take are written by software from other outlets’ reporting and may contain errors — always check the linked original."
}