Skip to main content

What does the useId() hook do?

What useId() does

useId() creates a unique, stable identifier (ID) that React guarantees is the same:

  • on the server (during SSR),
  • and on the client (during hydration).

This is needed for:

  • linking form elements (e.g. <label> and <input>),
  • generating ids in lists where you can't use the index,
  • creating unique aria-* attributes for accessibility (A11y),
  • generating identifiers in components that render many times.

Syntax

javascript
const id = useId();

useId() returns a string (e.g. "react-aria123") that is guaranteed unique within the whole application.

React guarantees:

  • uniqueness within the component tree;
  • stability across renders;
  • a predictable match between the server and the client during SSR.

Usage example

Correctly linking a label and an input:

javascript
function NameField() { const id = useId(); return ( <div> <label htmlFor={id}>Name:</label> <input id={id} type="text" /> </div> ); }

Why this matters:

  • If you used Math.random() or a UUID, the server and the client would produce different values, and React would print a warning during hydration:

    javascript
    Warning: Prop `id` did not match. Server: "a123" Client: "b456"

With useId(), this doesn't happen - React keeps the IDs in sync.


How React keeps IDs consistent

When rendering, React builds an ID from a deterministic prefix and counter, not a random number.

Example result:

javascript
id = react-aria123 id = react-aria124 id = react-aria125

And these values will be the same:

  • during server rendering (renderToString() / renderToPipeableStream()),
  • during hydration on the client (hydrateRoot()).

Example with several elements

javascript
function EmailForm() { const emailId = useId(); const passwordId = useId(); return ( <form> <label htmlFor={emailId}>Email</label> <input id={emailId} type="email" /> <label htmlFor={passwordId}>Password</label> <input id={passwordId} type="password" /> </form> ); }

Every call to useId() creates a new ID, but stable across renders.


Comparison with other approaches

MethodProblem
Math.random()Different values on the server and client -> a hydration warning
uuid()A random result -> breaks SSR determinism
A counter in the componentCan get out of sync with conditional rendering
useId()Deterministic, unique, SSR-friendly

Nested components

useId() also works well in nested components, because React builds hierarchical prefixes to avoid collisions between components.

Example:

javascript
function FormField({ label, children }) { const id = useId(); return ( <div> <label htmlFor={id}>{label}</label> {React.cloneElement(children, { id })} </div> ); } function Form() { return ( <FormField label="Name"> <input type="text" /> </FormField> ); }

React will produce a unique, nested ID so there are no collisions:

javascript
react-aria1-1 react-aria1-2 react-aria2-1

Using it together with CSS / aria

useId() is often used for accessibility attributes:

javascript
function Tooltip({ text, children }) { const tooltipId = useId(); return ( <> {React.cloneElement(children, { 'aria-describedby': tooltipId })} <span id={tooltipId} role="tooltip"> {text} </span> </> ); }

Accessible, unique, and free of collisions even under SSR.


Limitations

  1. useId() cannot be used inside loops / conditionals, otherwise the order of hook calls breaks (like any React hook).
  2. It is not meant for dynamic unique keys in lists. For map() keys you still need to use unique identifiers from the data.
  3. It doesn't suit generating global UUIDs - it's a tool only for stable IDs within the React tree.

Summary

useId() is a hook for generating unique, stable, and deterministic identifiers that match between the server and the client, which makes it invaluable for:

  • SSR + Hydration,
  • linking label ↔ input,
  • working with aria attributes and accessibility,
  • nested, repeating components.

In short:

PropertyValue
ReturnsA unique ID string
StabilityAcross renders and between server/client
SSR compatibilityYes
Uniqueness in the treeGuaranteed
Usable in a loopNo
Works withReact 18+
Typical scenariosForms, aria, SSR, nested components

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.