Hydration Proof

Search documentation

Find a page or section

HP5003: Focus was lost during hydration

HP5003 (focus-lost) means the field a user focused, or the text they selected, was lost while the page hydrated. Why it happens and how to keep focus.

HP5003 (focus-lost) means the element that had focus, or the text selected in it, was lost while the page hydrated. A user who started typing before the page was ready suddenly types into nothing. Avoid re-creating the focused element during hydration, and do not move focus in effects that run on load.

CodeHP5003
Namefocus-lost
Default severityWarning
GroupInteraction during hydration
What it meansThe focused element or the text selection was lost during hydration.

What the HP5003 focus lost finding means

The finding comes from the interaction checks (checks.interactions: true or --interactions). With the page's scripts held back, hydration-proof focuses the first text field and selects text in it, then lets the page hydrate and checks both again:

The text field that had focus before hydration lost it while the page hydrated.
The text selection in the field changed from 0-5 to 0-0 while the page hydrated.

Losing focus is reported with higher confidence than a changed selection. Both are warnings.

Likely causes

  • A hydration mismatch around the field: React throws away that branch and renders a new field, which has no focus. Look for HP1010 on the same page.
  • An effect that calls focus() on another element when the page loads, such as a search box or a dialog.
  • A value set on the field during hydration, which resets the selection (see HP5002).

How to fix it

Avoid re-creating the focused element during hydration (fix mismatches around it), and do not move focus in effects that run on load.

  1. Fix the hydration findings on the same page first; a replaced branch is the most common cause.
  2. Move focus only in response to a user action, or only when nothing else has focus yet:
components/search-box.tsx
"use client";
 
import { useEffect, useRef } from "react";
 
export function SearchBox() {
  const inputRef = useRef<HTMLInputElement>(null);
 
  useEffect(() => {
    // Before: inputRef.current?.focus() took focus from the field in use
    if (document.activeElement === document.body) {
      inputRef.current?.focus({ preventScroll: true });
    }
  }, []);
 
  return <input ref={inputRef} type="search" name="q" />;
}

When it is acceptable

If the page moves focus on purpose (a dialog that opens on load), ignore code: "HP5003" for that route with a reason; see ignoring findings.