React useId Explained: Generate Accessible IDs

useId returns a unique ID string for linking labels, hints, and inputs with accessibility attributes, even when a component renders many times.

6 min read

The useId Hook returns a unique ID string that stays stable for a given component instance. You pass that ID to accessibility attributes such as aria-describedby, so a label and its input stay connected even when the component renders several times on one page.

What useId returns

Call useId at the top level of your component. It takes no arguments and returns a string you can attach to id attributes.

PartBehavior
ArgumentsNone.
Return valueA unique ID string for this component instance.
StabilityStable across renders for the same mounted instance.

The ID is not a plain counter. React builds it from the component's position in the tree, which keeps server and client output identical when the two trees match.

Why an incrementing counter is not enough

A global counter like nextId++ looks simpler, but it breaks under server rendering. The order in which client components hydrate may not match the order the server emitted HTML, so the counter produces different IDs on each side. useId avoids that mismatch because it derives the ID from the parent path of the component.

Why hardcoded IDs fail

A hardcoded id attribute works once, then breaks the moment the component renders twice. Two elements with the same id confuse both browsers and assistive technology. Hardcoding also forces every consumer to coordinate on the same string, which breaks reuse.

App.jsxApp.jsx
import { useId } from "react";
 
function PasswordField() {
  const passwordHintId = useId();
  return (
    <>
      <input type="password" aria-label="Password" aria-describedby={passwordHintId} />
      <p id={passwordHintId}>Use at least 18 characters.</p>
    </>
  );
}

The input points to the hint through aria-describedby, and the hint carries the same generated id. If PasswordField renders twice, each copy gets its own ID, so the two inputs stay paired with the right hint. A screen reader reads the hint when the input receives focus.

Generate several IDs from one prefix

One call to useId can serve several related elements by adding suffixes.

App.jsxApp.jsx
import { useId } from "react";
function NameFields() {
  const id = useId();
  return (
    <>
      <label htmlFor={`${id}-first`}>First name</label>
      <input id={`${id}-first`} type="text" />
      <label htmlFor={`${id}-last`}>Last name</label>
      <input id={`${id}-last`} type="text" />
    </>
  );
}

The shared base keeps the two fields unique while each label still pairs with its own input. This avoids a separate useId call for every element.

What useId is not for

useId is for accessibility attributes, not for general identity. Do not generate list keys from it, because keys should come from your data. Do not generate cache keys from it, because the ID may change during rendering.

What to learn next

Accessible IDs pair naturally with live announcements. Continue with how to announce dynamic updates to screen readers, and see how to make clickable elements accessible for more label and focus patterns.

Rune AI

Rune AI

Key Insights

  • useId returns a unique ID string and takes no arguments.
  • Pass the ID to attributes like aria-describedby and htmlFor.
  • One call can serve several elements using suffixes.
  • Do not use useId for list keys or cache keys.
  • Server and client must render identical trees for IDs to match.
RunePowered by Rune AI

Frequently Asked Questions

What does useId return?

A unique ID string for a specific useId call in a specific component instance. It takes no arguments.

Can I use useId for list keys?

No. List keys should come from your data. useId is for accessibility attributes, not general identity.

Does useId work with server rendering?

Yes, as long as the server and client render identical component trees. React derives the ID from the component's position in the tree.

Conclusion

useId generates stable, unique ID strings for connecting related elements through accessibility attributes. Use one ID per component instance, or a shared prefix for several related elements.