# 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.

Source: https://hydration.jscrate.dev/docs/issues/hp5003
Last updated: 2026-09-18

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.

| | |
| --- | --- |
| Code | `HP5003` |
| Name | `focus-lost` |
| Default severity | Warning |
| Group | Interaction during hydration |
| What it means | The focused element or the text selection was lost during hydration. |

## What the HP5003 focus lost finding means

The finding comes from the [interaction checks](https://hydration.jscrate.dev/docs/interactions)
(`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:

```text
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](https://hydration.jscrate.dev/docs/issues/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](https://hydration.jscrate.dev/docs/issues/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:

```tsx title="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](https://hydration.jscrate.dev/docs/ignoring).

## Related

- [HP5002: user input was reset during hydration](https://hydration.jscrate.dev/docs/issues/hp5002)
- [HP5007: scroll position reset during hydration](https://hydration.jscrate.dev/docs/issues/hp5007)
- [Interaction and navigation checks](https://hydration.jscrate.dev/docs/interactions)
- [HP1010: React discarded server HTML for a branch](https://hydration.jscrate.dev/docs/issues/hp1010)
