Back to the Library

React Input Loses Focus on Every Keystroke - The Remount Bug

7 min read
“You click into the field, type one character, and focus jumps out. Click back in, type another character, focus jumps out again. The input isn't losing focus - it's being destroyed and rebuilt, one keystroke at a time.”

In This Post

How React Decides: Update or ReplaceCase One: A Component Defined Inside a ComponentThe Fix: Define It Once, OutsideCase Two: The `key` Prop Does the Same Thing on PurposeWhen the Key Change Is the Fix, Not the BugReading the Same Mechanism Two WaysPractice These Patterns

Practice This Pattern

Blue Belt

Can't Type Without Losing Focus

A signup form where every keystroke kicks focus out of the name field - the input component is defined inside the parent's function body, so React gets a new component type on every render.

+25 KI
Enter the Dojo
Blue Belt

Form Keeps Showing the Previous User

A user editor whose form fields stay locked on the first user no matter which user button is clicked - useState's initializer only runs once, and key={user.id} is the fix that forces it to run again.

+25 KI
Enter the Dojo
Blue Belt

Counter Resets While Typing

A session counter that resets to 0 on every keystroke in an unrelated filter field - the counter's key prop is built from the filter text, so it changes, and changes, and changes.

+25 KI
Enter the Dojo
BugDojo
BlogFAQ

© 2026. Carved in code.

Click into the input. Type "a". Focus disappears - the field is still there, but the cursor is gone, and the next keystroke does nothing until you click back in. Type "b". Focus disappears again.

Typing a full word is, practically, impossible.

This isn't a focus bug in the way it feels. Nothing is "stealing" focus. The input element you clicked into is being thrown away and replaced with a brand new one - and the new one, understandably, isn't focused.

How React Decides: Update or Replace

On every render, React compares what it rendered last time to what it's rendering now, element by element. For each element, it asks: is this the same type as last time, in the same position?

If yes, React updates the existing instance in place - same DOM node, same internal state, same focus, just with new prop values applied. If no, React unmounts the old instance entirely - destroying its DOM node and any state it held - and mounts a brand new one from scratch.

A focused <input> that gets unmounted and remounted is, simply, no longer focused. The new <input> is a different DOM node. Browsers don't transfer focus between DOM nodes for you.

Case One: A Component Defined Inside a Component

export default function SignupForm() {
  const [name, setName] = useState("");
 
  const NameField = ({ value, onChange }) => (
    <div>
      <label>Name</label>
      <input value={value} onChange={onChange} placeholder="Enter your name" />
    </div>
  );
 
  return (
    <div>
      <NameField value={name} onChange={(e) => setName(e.target.value)} />
      <p>Hello, {name || "stranger"}!</p>
    </div>
  );
}

NameField is defined inside SignupForm's function body. Every time SignupForm renders, this line runs again - and const NameField = (...) => ... creates a brand new function, at a brand new memory address, every single time.

Type one character. setName updates state. SignupForm re-renders. The line defining NameField runs again, producing function #2. React compares function #1 (last render's type for this element) against function #2 (this render's type). They are different functions - different references - even though they're defined identically. React treats this as "a different component type," unmounts the <NameField> from function #1 (destroying its <input> and any focus on it), and mounts a fresh one from function #2.

Symptom

Type one character, focus is gone, the value did update (you can see your character in the field after clicking back in) - but the cursor isn't there anymore. Every keystroke requires a fresh click into the field.

The Fix: Define It Once, Outside

const NameField = ({ value, onChange }) => (
  <div>
    <label>Name</label>
    <input value={value} onChange={onChange} placeholder="Enter your name" />
  </div>
);
 
export default function SignupForm() {
  const [name, setName] = useState("");
 
  return (
    <div>
      <NameField value={name} onChange={(e) => setName(e.target.value)} />
      <p>Hello, {name || "stranger"}!</p>
    </div>
  );
}

NameField now exists once, at module scope, created when the file loads. Every render of SignupForm refers to the exact same function reference. React's type comparison passes - same type as last time - so it updates the existing <NameField> and its <input> in place. Same DOM node. Same focus. Typing works.

The Sensei's Hint

This is specifically about components defined inside another component's body - functions, arrow functions, anything that creates a new component type on every render. A component defined at module scope, or imported from another file, doesn't have this problem no matter how many times the parent re-renders.

Case Two: The key Prop Does the Same Thing on Purpose

React's "same type, same position" rule has one more input: the key prop. Two elements of the same type with different keys are treated as different components - intentionally. This is usually exactly what you want, except when the key changes for reasons you didn't intend.

function ProgressCounter() {
  const [sessions, setSessions] = useState(0);
  return (
    <div>
      <p>Sessions: {sessions}</p>
      <button onClick={() => setSessions((s) => s + 1)}>Log Session</button>
    </div>
  );
}
 
export default function StudyTracker() {
  const [subject, setSubject] = useState("");
 
  return (
    <div>
      <input value={subject} onChange={(e) => setSubject(e.target.value)} />
      <ProgressCounter key={`progress-${subject}`} />
    </div>
  );
}

Log a few sessions - the counter goes up correctly. Now type one character into the filter input. The counter drops back to 0.

key={\progress-$`}tiesProgressCounter's identity to the filter text. Every keystroke changes subject, which changes the key, which tells React "this is a different ProgressCounternow" - so it unmounts the old one (with itssessions` state) and mounts a fresh one starting at 0.

<ProgressCounter key="progress" />

A stable key - one that never changes - means React always sees "the same ProgressCounter" across renders, regardless of what subject does. The existing instance updates in place, and sessions survives.

The Sensei's Hint

A changing key isn't always a bug - sometimes it's the fix. If a form's fields should fully reset whenever a different record is loaded, key={record.id} on the form intentionally forces a clean remount with fresh initial state for each record. The difference is whether the reset is something you want.

When the Key Change Is the Fix, Not the Bug

const USERS = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  { id: 3, name: "Carol" },
];
function ProfileForm({ user }) {
  const [name, setName] = useState(user.name);
  return (
    <div>
      <input value={name} onChange={(e) => setName(e.target.value)} />
      <p>Editing: {name}</p>
    </div>
  );
}
 
export default function UserEditor() {
  const [selectedId, setSelectedId] = useState(1);
  const user = USERS.find((u) => u.id === selectedId);
 
  return (
    <div>
      {USERS.map((u) => (
        <button key={u.id} onClick={() => setSelectedId(u.id)}>{u.name}</button>
      ))}
      <ProfileForm user={user} />
    </div>
  );
}

This one is the opposite problem. Click a different user button - the input keeps showing the first user's name. useState(user.name) only runs its initializer on the component's first mount; every render after that, React reuses the existing ProfileForm instance and its existing name state, completely ignoring that user is now a different object.

<ProfileForm key={user.id} user={user} />

Adding key={user.id} makes the fix: when user.id changes, React now treats this as a different ProfileForm - unmounts the old one, mounts a new one, and useState(user.name) runs its initializer fresh, with the new user's name.

Reading the Same Mechanism Two Ways

Cases one and three are about a key (or component type) changing when it shouldn't - causing unwanted resets. Case two's fix is the inverse: a key that was changing when it shouldn't, made stable. Same underlying rule - "does React see the same component identity as last render?" - producing opposite-looking bugs depending on which direction the identity is unstable.

Constraints

Component identity comes from two things together: the component's type (the function itself - watch out for functions defined inside other components) and its key (if one is given - watch out for keys built from values that change for unrelated reasons). If either one differs from the previous render, React remounts. State, DOM nodes, and focus do not survive a remount.

Practice These Patterns

"Can't Type Without Losing Focus" is the inline-component version - the purest form of this bug, and the one you'll recognize fastest in real codebases. "Form Keeps Showing the Previous User" flips it around: here, a missing key is the bug, and adding one is the fix - useful for seeing both directions of the same mechanism. "Counter Resets While Typing" closes the loop with an unstable key causing unwanted remounts on every keystroke - work through all three to get comfortable with "what makes React think this is a new component?" from every angle.