useId for Stable Unique IDs

useId is a React Hook that generates a unique, stable string ID you can attach to HTML elements so labels, inputs, and accessibility attributes stay correctly wired together across renders and between server and client. It exists to solve one specific problem: producing IDs that don't break hydration or change on every render the way Math.random() does.

Learn useId for Stable Unique IDs in our free React course — an interactive lesson with runnable examples, a practice exercise and a quick recall.

Part of the free React course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

1️⃣ The Problem: IDs That Won't Sit Still

A only works if its htmlFor exactly matches the input's id . Generate that id with Math.random() or a module-level counter and it can differ between the server-rendered HTML and the client render, causing a hydration mismatch warning and broken screen-reader behavior.

2️⃣ The Fix: useId at the Top of the Component

Call useId() once at the top of your component, like any other hook. It returns a stable, opaque string (something like :r0: ) that is identical on server and client and unchanged across re-renders. Use that one value for both the htmlFor and the id .

3️⃣ Many Elements: One Call, Suffixed IDs

React intends one useId per component . When a component has several elements that each need an id — input, hint, error — call useId once and append suffixes. This keeps everything grouped and still SSR-stable, and it's how you wire aria-describedby .

These lines build an accessible labeled input with a hint. Put them in the right order:

📋 Quick Reference

It changes every render and differs between server and client, causing a hydration mismatch and breaking the label-to-input link. useId is stable and SSR-safe.

2. You need ids for an input AND its error message. How many useId calls?

One. Call useId() once, then use id for the input and for the message.

No. Keys come from your data. useId gives one id per component instance and can't tell items apart.

From one base id, produce the matching label, input, and description ids that connect an accessible field. Run it and check your output.

Practice quiz

What does the useId Hook return?

  • A random number that changes on every render
  • An incrementing integer counter
  • A stable, unique, opaque string ID tied to the component instance
  • The component's display name as a string

Answer: A stable, unique, opaque string ID tied to the component instance. useId returns a stable, opaque string (like ':r0:') that stays the same across renders and matches between server and client.

What is the main problem useId was created to solve?

  • IDs that differ between server and client, causing hydration mismatches
  • Generating list keys from array data
  • Memoizing expensive calculations
  • Fetching data on the server

Answer: IDs that differ between server and client, causing hydration mismatches. useId produces IDs that are identical on server and client, so it avoids the hydration mismatch that Math.random() or counters cause.

Why should you NOT use Math.random() to generate an element id?

  • It is too slow to call
  • It only returns whole numbers
  • React forbids calling it inside components
  • It changes every render and differs between server and client

Answer: It changes every render and differs between server and client. A random id changes on each render and won't match between server and client, breaking the label/input link and causing a hydration warning.

Can a useId value be used as a React list key?

  • Yes, it is designed specifically for keys
  • No — keys must come from your data; useId gives one id per component instance
  • Yes, but only inside a .map() call
  • Only if you append the array index

Answer: No — keys must come from your data; useId gives one id per component instance. useId is for HTML id/aria attributes, not keys. Keys must come from stable data fields, since useId can't distinguish items in a list.

You need ids for an input AND its error message. How many useId calls?

  • One, then append suffixes like id + '-error'
  • Two — one per element
  • Zero — reuse the same hard-coded string
  • One call per render cycle

Answer: One, then append suffixes like id + '-error'. React intends one useId per component. Call it once and add suffixes (id + '-error', id + '-hint') for the related elements.

Where should you call useId in a component?

  • Inside an event handler
  • Inside a loop, once per item
  • At the top level of the component, like any other Hook
  • Inside a conditional so it only runs sometimes

Answer: At the top level of the component, like any other Hook. Like all Hooks, useId must be called at the top level of the component, not inside loops, conditions, or event handlers.

How do you wire a label to an input so clicking the label focuses the input?

  • Set the input's name to the label's text
  • Match the label's htmlFor to the input's id using the same useId value
  • Wrap both in a <div> with the same className
  • Give them the same key prop

Answer: Match the label's htmlFor to the input's id using the same useId value. The label's htmlFor must exactly match the input's id. Using one useId value for both keeps them wired together and SSR-stable.

What is true about the exact string useId returns (e.g. ':r0:')?

  • You should parse it to extract a number
  • It is guaranteed to be a valid CSS class name only
  • It must be manually incremented by you
  • It is intentionally opaque — never hard-code or parse it, just pass it through

Answer: It is intentionally opaque — never hard-code or parse it, just pass it through. The string is opaque and may change format across React versions. Just pass it to id, htmlFor, and aria attributes.

Which accessibility attribute commonly uses a suffixed useId value?

  • data-testid for testing
  • aria-describedby pointing at a hint or error element's id
  • tabindex to control focus order
  • role to declare the element type

Answer: aria-describedby pointing at a hint or error element's id. aria-describedby={id + '-hint'} points the input at its hint/error element, wiring accessible descriptions with a suffixed id.

Is useId a stable, supported public API in modern React?

  • No, it is experimental and may be removed
  • It only works in React Native
  • Yes — it is a stable Hook introduced in React 18 for generating unique IDs
  • It is a class-component method, not a Hook

Answer: Yes — it is a stable Hook introduced in React 18 for generating unique IDs. useId was added as a stable Hook in React 18 and is the recommended way to generate unique IDs that are SSR-safe.