Preserving and Resetting State in React
How React preserves or resets state depends on component position and keys. Learn why state disappears and how to fix it as well as links to more resources!

When building accessible React applications, one of the most common requirements
is to ensure that form fields and their labels are properly associated. This
usually means giving each input a unique id
and making sure the corresponding
<label>
uses the same value in its htmlFor
attribute. But if you've ever
tried to build a reusable form component, you know that generating unique IDs
can be a pain.
Let's talk about why this matters, the problems with common approaches, and how
React's useId
hook makes it all so much easier.
Imagine you have a form with several fields:
function ContactForm() { return ( <form> <label htmlFor="name">Name</label> <input id="name" type="text" /> <label htmlFor="email">Email</label> <input id="email" type="email" /> </form> )}
This works fine—until you want to reuse a field component multiple times, or
render a list of fields dynamically. If you hardcode the id
and then reuse
that component, you end up with duplicate IDs, which is invalid HTML and breaks
accessibility for screen readers.
One easy way to tell whether a label is associated with an input is to click on the label and see if the input is focused. If it is, then the label is associated with the input. If it's not, then you've got work to do.
A common workaround is to generate a random string for each field:
function Field({ label, ...props }) { const id = Math.random().toString(36).slice(2) return ( <div> <label htmlFor={id}>{label}</label> <input id={id} {...props} /> </div> )}
This seems to work, but it introduces subtle bugs:
useId
: The React WayReact 18 introduced the useId
hook to solve this exact problem. It generates a
unique, stable ID for each component instance, and it works seamlessly with SSR.
Here's how you use it:
import { useId } from 'react'function Field({ label, ...props }) { const id = useId() return ( <div> <label htmlFor={id}>{label}</label> <input id={id} {...props} /> </div> )}
Now, every time you render <Field label="Name" />
, it gets a unique, stable
ID. You can safely use this component multiple times—even in lists or dynamic
forms—without worrying about duplicate IDs or hydration mismatches.
useId
It's important to note that useId
is only for DOM relationships, like
associating a label with an input. Don't use it for React keys in lists:
const key = useId()// ❌ Don't do this!return <div key={key}>{item}</div>
React keys should come from your data, not from useId
. The ID from useId
is
only stable for the lifetime of a component instance, not across renders or data
changes.
useId
gives you a stable, unique ID for each component instance, the React
way.useId
for DOM relationships, not for React keys.For more, check out the official React docs on useId.
You'll learn this and other accessibility tips in the workshops on EpicReact.dev.
Delivered straight to your inbox.
How React preserves or resets state depends on component position and keys. Learn why state disappears and how to fix it as well as links to more resources!
How web app architecture evolved from full page reloads to React Server Components, and why each step in this journey mattered for developers and users.
Control Props let you hand over component state to your users—just like a controlled input. Learn to build ultra-flexible UIs at EpicReact.dev.
A developer shares how a tiny tip from Epic React—using the key prop to reset component state—saved him from a gnarly state bug in a complex React Native app.