Skip to main content

React useId — Complete In-Depth Guide


1. Introduction

What is useId?

useId is a built-in hook in React that generates stable, unique IDs for components.
const id = useId();
These IDs are:
  • Unique across the entire React tree
  • Consistent between server and client
  • Safe for accessibility attributes

Why is it important?

Before useId, developers often:
  • Used random IDs (Math.random())
  • Used incremental counters
  • Faced hydration mismatches in SSR
useId solves:
  • SSR consistency issues
  • Accessibility linking (label ↔ input)
  • Avoiding ID collisions in reusable components

When and why we use it

Use useId when:
  • Linking elements via attributes:
    • labelinput
    • aria-describedby
    • aria-labelledby
  • Building reusable component libraries
  • Supporting Server-Side Rendering (SSR)
❌ Do NOT use it for:
  • Keys in lists
  • Database IDs
  • Business logic identifiers

2. Concepts / Internal Workings

Core Concept: Stable Unique ID

Unlike random IDs, useId generates:
  • Deterministic IDs
  • Based on React’s internal rendering tree
Example output:
: r0 :
: r1 :

How it works internally

React assigns IDs based on:
  • Component position in the tree
  • Render order
  • Internal fiber structure

Key idea:

The same component tree → same IDs (even across server and client)
This ensures:
  • No mismatch during hydration
  • Predictable ID generation

SSR & Hydration Behavior

Without useId:
const id = Math.random(); // ❌ mismatch between server & client
With useId:
const id = useId(); // ✅ consistent across environments
React ensures:
  • Server-generated ID = Client-generated ID
  • Prevents hydration warnings

Relationship with other React features

1. Concurrent Rendering

  • IDs remain stable even if rendering is interrupted or restarted

2. Server Components

  • Works seamlessly with streaming SSR

3. Component Reusability

  • Each instance gets its own scoped ID

4. Hooks System

  • Follows same rules:
    • Must be called at top level
    • Order must not change

3. Syntax & Examples


Basic Usage

import { useId } from "react";

function InputField() {
  const id = useId();

  return (
    <>
      <label htmlFor={id}>Name</label>
      <input id={id} type="text" />
    </>
  );
}

Multiple IDs from one hook

function Form() {
  const id = useId();

  return (
    <>
      <label htmlFor={`${id}-name`}>Name</label>
      <input id={`${id}-name`} />

      <label htmlFor={`${id}-email`}>Email</label>
      <input id={`${id}-email`} />
    </>
  );
}

Accessibility Example (aria-describedby)

function PasswordField() {
  const id = useId();

  return (
    <>
      <input aria-describedby={`${id}-hint`} />
      <p id={`${id}-hint`}>Must be at least 8 characters</p>
    </>
  );
}

Reusable Component Example

function TextInput({ label }) {
  const id = useId();

  return (
    <div>
      <label htmlFor={id}>{label}</label>
      <input id={id} />
    </div>
  );
}
Usage:
<TextInput label="Username" />
<TextInput label="Email" />
Each instance gets a unique ID automatically.

Combining with Props (Advanced)

function Input({ id: customId, label }) {
  const generatedId = useId();
  const id = customId || generatedId;

  return (
    <>
      <label htmlFor={id}>{label}</label>
      <input id={id} />
    </>
  );
}

4. Edge Cases / Common Mistakes


❌ Using useId for list keys

items.map(item => <div key={useId()} />); // ❌ WRONG
Why it fails:
  • Keys must be stable across renders
  • useId changes if component structure changes
✅ Correct:
items.map(item => <div key={item.id} />);

❌ Calling inside loops/conditions

if (condition) {
  const id = useId(); // ❌ breaks hook rules
}
Why:
  • Hook order must remain consistent

❌ Expecting global uniqueness across apps

  • IDs are unique within a React tree
  • Not guaranteed across multiple independent React roots

⚠️ ID mismatch due to conditional rendering

function Component({ show }) {
  const id1 = useId();
  const id2 = show ? useId() : null; // ❌ dangerous
}
Problem:
  • Hook order changes → incorrect ID mapping

⚠️ Styling misuse

#id-123 { color: red; } // ❌ not reliable
Why:
  • IDs are not predictable or stable for styling

⚠️ Using in non-accessibility scenarios

const id = useId();
fetch(`/api/data/${id}`); // ❌ misuse
IDs are not meant for:
  • API calls
  • Business logic

5. Best Practices


✅ Use for accessibility first

Primary purpose:
  • labelinput
  • ARIA attributes

✅ Prefix IDs for clarity

const id = useId();

<input id={`${id}-email`} />
Improves:
  • Debugging
  • Readability

✅ Allow overriding IDs

Useful for libraries:
const id = props.id || useId();

✅ Keep hook calls stable

Always:
const id = useId(); // top-level
Never:
  • Inside loops
  • Inside conditions

✅ Avoid overuse

Use only when needed:
  • Not every component requires an ID

Performance Considerations

  • useId is lightweight
  • No significant performance overhead
  • Safer than custom ID generation

Coding Conventions

  • Use suffix pattern:
    • ${id}-label
    • ${id}-input
    • ${id}-hint
  • Keep IDs internal to component

Summary

useId is a small but critical hook for modern React applications:
  • Solves SSR hydration issues
  • Ensures consistent unique IDs
  • Enables accessible UI development
  • Supports concurrent rendering safely
Think of useId as an accessibility + SSR safety tool, not a general-purpose ID generator.

Below is a senior-level, high-signal interview set for useId in React. Each question probes design reasoning, internals, and real-world trade-offs.

1. Why was useId introduced when developers could already generate IDs manually?

Answer

At a glance, generating IDs seems trivial (Math.random(), counters). The real problem appears in SSR + hydration consistency.

Core Issue:

  • Server renders: id="abc123"
  • Client renders: id="xyz789"
  • React detects mismatch → hydration warning or full re-render

Why useId solves this:

  • IDs are deterministic based on component tree position
  • Same tree → same IDs → safe hydration

Comparison:

ApproachProblem
Math.random()Non-deterministic
Increment counterBreaks with concurrency / SSR
useIdDeterministic + SSR-safe

Key Insight:

useId is not about uniqueness — it’s about consistency across renders and environments.

2. How does useId remain stable across server and client renders?

Answer

Internally, React assigns IDs based on:
  • Fiber tree traversal path
  • Hook call order

Simplified model:

App
 ├─ Form (useId → r0)
 ├─ Input (useId → r1)
React encodes this path into the ID.

Why this works:

  • Server and client render the same tree
  • Hook order is identical
  • IDs are reconstructed identically

Important implication:

Any change in render structure or hook order can break ID consistency.

3. Why is useId safe in Concurrent Rendering but counters are not?

Answer

Concurrent rendering allows:
  • Interruptions
  • Partial renders
  • Re-renders before commit

Counter problem:

let counter = 0;
const id = counter++;
  • If rendering restarts → counter increments again → mismatch

useId behavior:

  • Based on logical tree position, not execution count
  • Restarting render produces the same ID

Key takeaway:

useId is idempotent, counters are stateful side effects.

4. Why should useId never be used as a key in lists?

Answer

Keys must:
  • Represent identity of data
  • Be stable across renders

Problem:

items.map(item => <div key={useId()} />)
  • useId depends on component position
  • Reordering list → keys change → React remounts everything

Correct:

items.map(item => <div key={item.id} />)

Insight:

Keys are about data identity, useId is about UI linkage.

5. What happens if you conditionally call useId?

Answer

This violates Rules of Hooks and breaks ID mapping.
if (show) {
  const id = useId(); // ❌
}

Why it breaks:

  • Hook order shifts
  • React associates wrong IDs to components

Subtle bug:

  • No immediate crash
  • Incorrect accessibility linking

Fix:

const id = useId();
if (!show) return null;

6. How does useId behave in deeply nested reusable components?

Answer

Each component instance gets a unique ID based on its position.
<TextInput />
<TextInput />
Produces:
:r0:
:r1:

Important:

  • Even if reused → no collision
  • No global registry needed

Insight:

IDs are scoped implicitly by tree position, not globally tracked.

7. Why is useId not suitable for business logic or API identifiers?

Answer

Because:
  • IDs are not stable across app reloads
  • IDs depend on render structure

Example:

fetch(`/api/user/${useId()}`); // ❌
If UI structure changes → ID changes → broken API calls

Correct:

  • Use database IDs
  • Use UUIDs for persistence

Principle:

useId is for render-time identity, not domain identity.

8. How does useId handle multiple IDs in a single component?

Answer

You derive multiple IDs from a base:
const id = useId();

<input id={`${id}-input`} />
<p id={`${id}-hint`} />

Why this is preferred:

  • Ensures all related elements stay grouped
  • Avoids multiple hook calls

Trade-off:

  • Slight manual string composition

9. What problems arise if the component tree differs between server and client?

Answer

Example:
{isClient && <ComponentWithUseId />}

Server:

  • Component not rendered → no ID

Client:

  • Component rendered → new ID

Result:

  • Hydration mismatch

Fix:

  • Ensure same tree on server and client
  • Or defer rendering with useEffect

10. Why does React prefix IDs with values like :r0:?

Answer

These prefixes:
  • Encode render tree position
  • Avoid collisions across roots

Internal purpose:

  • Fast lookup during hydration
  • Deterministic reconstruction

Insight:

IDs are not meant for humans — they’re optimized for React internals.

11. Can useId guarantee uniqueness across multiple React roots?

Answer

No. Each root:
createRoot(...)
Has its own ID namespace.

Problem:

  • Two apps on same page → possible collisions

Solution:

  • Prefix manually if needed:
const id = `app1-${useId()}`;

12. How does useId interact with accessibility APIs?

Answer

Primary use case:
<label htmlFor={id}>Email</label>
<input id={id} />

Why important:

  • Screen readers rely on ID linking
  • ARIA relationships require stable IDs

Example:

<input aria-describedby={`${id}-hint`} />

Insight:

useId is fundamentally an accessibility enabler, not just a utility.

13. What are the trade-offs of useId vs UUID libraries?

Answer

AspectuseIdUUID
SSR-safe
Deterministic
Persistent
Global unique

Decision:

  • UI linking → useId
  • Persistent identity → UUID

14. Why is useId considered a “render-phase” API?

Answer

It:
  • Runs during render
  • Does not cause side effects
  • Returns deterministic value

Contrast with:

useEffect(() => generateId())
  • Runs after render
  • Causes mismatch risk

Insight:

Anything affecting markup must be resolved during render, not after.

15. How does useId behave with Suspense and streaming SSR?

Answer

React ensures:
  • IDs remain consistent even if components load later

Mechanism:

  • React reserves ID ranges for suspended components

Benefit:

  • Streaming HTML still hydrates correctly

Insight:

useId is designed for future React architecture, not just current needs.

16. What subtle bugs can arise when refactoring components using useId?

Answer

Example:
  • Moving a component in the tree

Before:

Form → Input (id: r1)

After:

Form → Wrapper → Input (id: r2)

Impact:

  • ID changes
  • Breaks tests or DOM queries

Lesson:

IDs are tied to structure, not component identity.

17. When would you allow consumers to override a useId value?

Answer

In reusable libraries:
function Input({ id: customId }) {
  const generatedId = useId();
  const id = customId || generatedId;
}

Why:

  • Integration with external systems
  • Testing
  • Accessibility control

18. Why is useId considered safer than useRef for IDs?

Answer

You could do:
const idRef = useRef(Math.random());

Problem:

  • Not SSR-safe
  • Different on server/client

useId:

  • Built for SSR consistency

Insight:

useRef stores values — useId guarantees cross-environment determinism.

19. What debugging challenges can useId introduce?

Answer

Issues:
  • IDs are opaque (:r1:)
  • Change when tree changes

Impact:

  • Harder DOM debugging
  • Snapshot test instability

Mitigation:

const id = `email-${useId()}`;

20. If React didn’t provide useId, how would you design a solution?

Answer

You would need:
  1. Deterministic tree traversal
  2. Stable hook ordering
  3. SSR + hydration alignment
  4. Concurrency-safe generation

Possible approach:

  • Tree-based hashing
  • Context-based ID propagation

But:

  • Extremely complex
  • Easy to break with concurrency

Final insight:

useId encapsulates a non-trivial distributed rendering problem into a simple API.

Below is a senior-level MCQ set on useId in React designed to test deep reasoning, internals, and edge cases.

1. Which scenario best explains why useId is preferred over Math.random()?

A. It generates shorter IDs B. It guarantees global uniqueness across apps C. It ensures consistency between server and client renders D. It avoids re-renders Correct Answer: C

Why:

useId generates deterministic IDs based on the component tree, ensuring SSR and client hydration match.

Why others are wrong:

  • A: Length is irrelevant
  • B: Not globally unique across roots
  • D: It does not affect re-renders

2. What happens if you use useId inside a loop to generate keys?

items.map(() => <div key={useId()} />)
A. Works fine if list is static B. Causes hydration mismatch only in SSR C. Breaks hook rules and causes unstable keys D. Generates globally unique keys safely Correct Answer: C

Why:

  • Hooks cannot be called inside loops
  • Keys become unstable → React remounts elements

Why others are wrong:

  • A: Still violates hook rules
  • B: Problem exists even in CSR
  • D: Keys are not stable

3. Why does useId remain stable in Concurrent Rendering?

A. It caches IDs in state B. It uses global counters with locks C. It derives IDs from component tree position D. It delays ID generation until commit phase Correct Answer: C

Why:

IDs are derived from fiber tree position + hook order, making them deterministic.

Why others are wrong:

  • A: No state involved
  • B: No global counter
  • D: Generated during render, not commit

4. What subtle bug can occur when conditionally calling useId?

A. Duplicate IDs B. Memory leak C. Hook order mismatch leading to wrong IDs D. Infinite re-render loop Correct Answer: C

Why:

Hook order changes → React assigns IDs incorrectly to components.

Why others are wrong:

  • A: Not primary issue
  • B: No memory leak
  • D: No render loop triggered

5. Which scenario can cause hydration mismatch even when using useId?

A. Using multiple useId calls B. Rendering different component trees on server vs client C. Using string concatenation with IDs D. Using useId in nested components Correct Answer: B

Why:

useId depends on identical tree structure. Differences break determinism.

Why others are wrong:

  • A: Safe
  • C: Safe
  • D: Supported use case

6. Why is useId unsuitable for API identifiers?

A. IDs are too long B. IDs are not persistent across renders or deployments C. IDs are not unique D. IDs cannot be serialized Correct Answer: B

Why:

IDs depend on render structure → not stable for business logic.

Why others are wrong:

  • A: Length irrelevant
  • C: Unique within tree
  • D: They are strings

7. What happens if a component using useId is moved in the tree?

A. ID remains same B. ID becomes undefined C. ID changes due to new tree position D. React throws an error Correct Answer: C

Why:

ID is derived from position in tree, not component identity.

Why others are wrong:

  • A: Incorrect assumption
  • B: Never undefined
  • D: No error thrown

8. How does useId behave across multiple React roots?

A. Guarantees uniqueness globally B. Shares ID pool across roots C. IDs may collide across roots D. Throws warning if duplicate IDs exist Correct Answer: C

Why:

Each root has its own namespace.

Why others are wrong:

  • A/B: No global coordination
  • D: React doesn’t warn

9. Which is the safest way to generate multiple related IDs?

A.
const id1 = useId();
const id2 = useId();
B.
const id = useId();
C.
const base = useId();
const id1 = base + "-a";
const id2 = base + "-b";
D.
const ids = Array(2).fill(useId());
Correct Answer: C

Why:

Single base ID ensures consistency and grouping.

Why others are wrong:

  • A: Multiple calls unnecessary
  • B: Only one ID
  • D: Incorrect hook usage

10. Why is useRef with Math.random() inferior to useId?

A. useRef causes re-renders B. Math.random() is slow C. Not SSR-safe → mismatch between server and client D. useRef cannot store strings Correct Answer: C

Why:

Random values differ between server and client.

Why others are wrong:

  • A: useRef doesn’t trigger re-render
  • B: Performance irrelevant
  • D: False

11. What is React’s primary design goal behind useId?

A. Simplify styling B. Improve debugging C. Enable deterministic UI identity for SSR + accessibility D. Replace UUID libraries Correct Answer: C

Why:

Core focus: SSR-safe + accessibility linking

Why others are wrong:

  • A/B: Secondary concerns
  • D: Not a replacement

12. Which scenario best justifies allowing an id prop override?

A. To reduce bundle size B. To improve rendering speed C. To integrate with external systems or testing D. To avoid using hooks Correct Answer: C

Why:

Consumers may need control over ID for integration/testing.

13. What happens if you use useId inside a custom hook incorrectly?

A. Nothing B. IDs become globally unique C. Hook order mismatch if conditionally used D. React ignores the hook Correct Answer: C

Why:

Same hook rules apply → order must remain stable.

14. Why are useId values prefixed with patterns like :r1:?

A. For readability B. To encode component tree position C. For CSS targeting D. To reduce string length Correct Answer: B

Why:

Encodes fiber path for deterministic reconstruction.

15. Which situation can silently break accessibility despite using useId?

A. Using multiple IDs B. Incorrectly linking htmlFor and id C. Using nested components D. Using string templates Correct Answer: B

Why:

Even correct IDs fail if not properly linked.

16. How does useId behave with Suspense?

A. IDs are regenerated after suspension B. IDs may become undefined C. React ensures consistent IDs despite delayed rendering D. Suspense disables useId Correct Answer: C

Why:

React reserves ID slots to maintain consistency.

17. What is a key trade-off of useId?

A. Slower rendering B. IDs are tied to structure, not component identity C. Cannot be used in functional components D. Requires memoization Correct Answer: B

Why:

Changing structure → changes ID

18. When debugging, why can useId be problematic?

A. Causes crashes B. IDs are unpredictable and change with structure C. Blocks DevTools D. Slows down rendering Correct Answer: B

Why:

IDs are opaque and not stable across refactors

19. Which is the best mental model for useId?

A. Global ID generator B. Persistent identifier C. Deterministic render-based identifier D. Random string generator Correct Answer: C

Why:

IDs are derived from render tree structure

20. What is the biggest mistake developers make with useId?

A. Using it too often B. Using it for styling C. Treating it as a general-purpose ID generator D. Calling it in functional components Correct Answer: C

Why:

Misusing it for business logic leads to unstable systems

Final Insight

A senior-level understanding of useId is not about:
“It generates unique IDs”
It is about:
Deterministic identity tied to render structure, designed for SSR correctness and accessibility.

Below is a senior-level coding problem set on useId in React. Each problem reflects real-world scenarios, focusing on SSR safety, accessibility, and architectural thinking.

1. Accessible Form Builder

Problem

Build a reusable <FormField /> component that:
  • Automatically links label and input
  • Supports error and hint text

Constraints

  • Must use useId
  • Should allow optional id override
  • Must support multiple instances safely

Expected Behavior

<FormField label="Email" hint="Enter valid email" />
Produces:
  • Proper htmlFor
  • aria-describedby linking hint/error

Edge Cases

  • No hint/error
  • Custom ID passed
  • Multiple fields

Solution Approach

  1. Generate base ID using useId
  2. Derive:
    • ${id}-input
    • ${id}-hint
  3. Conditionally attach ARIA attributes
const baseId = useId();
const id = props.id || baseId;
Key Insight: Single base ID → multiple related elements

2. Dynamic Field Array (Form Builder)

Problem

Render a dynamic list of inputs (add/remove fields) with correct label associations.

Constraints

  • No useId inside loops incorrectly
  • Must avoid key misuse

Expected Behavior

  • Adding/removing fields does not break label-input mapping

Edge Cases

  • Reordering fields
  • Removing middle field

Solution Approach

  • Each field is a component:
function Field() {
  const id = useId();
}
  • Parent handles list keys using data IDs
Key Insight:
useId inside component, NOT inside .map()

3. SSR-Safe Modal Accessibility

Problem

Build a modal with:
  • aria-labelledby
  • aria-describedby

Constraints

  • Must work in SSR
  • IDs must match server/client

Expected Behavior

  • Screen readers correctly announce modal content

Edge Cases

  • Modal conditionally rendered
  • Multiple modals

Solution

const id = useId();

<h2 id={`${id}-title`} />
<p id={`${id}-desc`} />
<div aria-labelledby={`${id}-title`} />
Key Insight: Avoid conditional hook calls → always call useId

4. Reusable Input Library Component

Problem

Create <Input /> that:
  • Accepts optional id
  • Falls back to useId

Constraints

  • Must not override user-provided ID

Edge Cases

  • External form libraries passing IDs

Solution

const generatedId = useId();
const id = props.id ?? generatedId;
Insight: Library components must allow controlled + uncontrolled ID

5. Tooltip System

Problem

Build tooltip that links trigger and tooltip content.

Expected Behavior

  • aria-describedby connects trigger to tooltip

Edge Cases

  • Multiple tooltips on page

Solution

const id = useId();

<button aria-describedby={id} />
<div role="tooltip" id={id} />
Insight: Tooltip IDs must be unique per instance

6. Nested Components ID Collision

Problem

Create nested components where both parent and child use useId.

Constraint

  • IDs must not collide

Expected Behavior

  • All IDs remain unique

Solution

React ensures uniqueness via tree structure. Insight: No need for global registry

7. Conditional Rendering Bug Fix

Problem

Fix:
if (show) {
  const id = useId();
}

Expected Behavior

  • No hook rule violation

Solution

const id = useId();
if (!show) return null;
Insight: Hooks must be unconditional

8. Multi-Input Component (Grouped IDs)

Problem

Create component with:
  • First name
  • Last name

Constraint

  • Single useId

Solution

const base = useId();

<input id={`${base}-first`} />
<input id={`${base}-last`} />
Insight: Group related inputs under one base ID

9. Server vs Client Mismatch Debugging

Problem

Fix hydration warning caused by:
const id = Math.random();

Expected Behavior

  • No mismatch warning

Solution

Replace with:
const id = useId();
Insight: Deterministic generation is required

10. Accessible Checkbox Group

Problem

Build checkbox group with:
  • Shared label
  • Individual inputs

Expected Behavior

  • Screen readers understand grouping

Solution

const id = useId();

<fieldset aria-labelledby={`${id}-legend`}>
  <legend id={`${id}-legend`} />
</fieldset>
Insight: useId helps group-level accessibility

11. Multiple React Roots Conflict

Problem

Two apps rendered on same page → avoid ID collision

Solution

const id = `app1-${useId()}`;
Insight: Manually namespace IDs when needed

12. Snapshot Testing Stability

Problem

Snapshots fail due to changing IDs

Constraint

  • Tests should be stable

Solution

  • Mock useId in tests OR
  • Prefix IDs
Insight: IDs tied to structure → unstable snapshots

13. Drag-and-Drop Form Builder

Problem

Fields reorder dynamically

Constraint

  • Accessibility must remain correct

Solution

  • Keep useId inside field component
  • Use stable keys for list
Insight: Separation of concerns:
  • Keys → data identity
  • IDs → accessibility

14. ARIA Live Region Component

Problem

Build live region for announcements

Solution

const id = useId();

<div id={id} aria-live="polite" />
Insight: Assistive tech relies on stable IDs

15. Custom Hook for Form Fields

Problem

Create useFormField() hook returning IDs

Solution

function useFormField() {
  const base = useId();
  return {
    inputId: `${base}-input`,
    hintId: `${base}-hint`,
  };
}
Insight: Encapsulate ID logic cleanly

16. Portal-Based Component (Modal/Tooltip)

Problem

Tooltip rendered via portal still needs correct ID linking

Constraint

  • Different DOM subtree

Solution

  • useId still works because it’s React-tree-based
Insight: IDs are tied to React tree, not DOM tree

17. Refactor Breaking IDs

Problem

Moving component changes ID unexpectedly

Expected Behavior

  • Avoid breaking functionality

Solution

  • Allow manual ID override
Insight: IDs depend on tree structure → fragile under refactor

18. Form Library Integration (React Hook Form / Formik)

Problem

Integrate useId with external library

Constraint

  • Library controls input props

Solution

<input id={id} {...register("email")} />
Insight: useId complements, not replaces form libraries

19. Accessibility Audit Fix

Problem

Audit reveals missing label associations

Solution

  • Introduce useId in all reusable inputs
Insight: Retrofitting accessibility is a real-world use case

20. Design a FieldWrapper System

Problem

Create system that:
  • Wraps any input
  • Automatically wires label, error, hint

Solution Steps

  1. Generate base ID
  2. Clone child with injected id
  3. Attach ARIA attributes
const id = useId();
const childWithId = cloneElement(child, { id });
Insight: Advanced composition pattern using useId

Final Takeaway

These problems test whether you understand:
  • Deterministic rendering vs random generation
  • Accessibility-first design
  • SSR + hydration constraints
  • Separation of identity (keys vs IDs)

Below are real-world debugging challenges around useId in React — the kind you’d see in code reviews or production incidents. Each case focuses on subtle bugs, incorrect assumptions, and React internals.

1. ❌ Hydration Mismatch Despite Using useId

Buggy Code

function Input({ showHint }) {
  const id = useId();

  return (
    <>
      <input aria-describedby={showHint ? `${id}-hint` : undefined} />
      {showHint && <p id={`${id}-hint`}>Hint</p>}
    </>
  );
}

What’s Wrong

Hydration mismatch occurs when showHint differs between server and client.

Why It Happens

  • useId depends on consistent tree structure
  • Conditional rendering changes tree → mismatch

Fix

const id = useId();

<p id={`${id}-hint`} hidden={!showHint}>Hint</p>
<input aria-describedby={`${id}-hint`} />

Best Practice

Keep DOM structure stable across SSR and client

2. ❌ Using useId for Keys

Buggy Code

items.map(item => <Row key={useId()} />)

What’s Wrong

  • Violates hook rules
  • Keys unstable → re-mounts

Why

  • Hooks must not be called inside loops
  • Keys must represent data identity

Fix

items.map(item => <Row key={item.id} />)

Best Practice

useId ≠ key generator

3. ❌ Conditional Hook Call

Buggy Code

if (isVisible) {
  const id = useId();
}

What’s Wrong

Hook order breaks

Why

React relies on consistent hook ordering

Fix

const id = useId();
if (!isVisible) return null;

Best Practice

Never call hooks conditionally

4. ❌ Incorrect Refactoring Breaks IDs

Buggy Code

function Wrapper() {
  return <Input />;
}
Moved from:
<Form>
  <Input />
</Form>

What’s Wrong

ID changes after refactor

Why

IDs depend on tree position

Fix

Allow override:
<Input id="email-input" />

Best Practice

Do not rely on useId for stable external references

5. ❌ Multiple useId Instead of Base ID

Buggy Code

const inputId = useId();
const hintId = useId();

What’s Wrong

Unnecessary multiple IDs

Why

  • Breaks logical grouping
  • Harder to debug

Fix

const base = useId();
const inputId = `${base}-input`;
const hintId = `${base}-hint`;

Best Practice

Derive multiple IDs from a single base

6. ❌ Using Math.random() with useId

Buggy Code

const id = useId() + Math.random();

What’s Wrong

Breaks determinism

Why

Random value differs between server/client

Fix

const id = useId();

Best Practice

Never mix randomness with useId

7. ❌ ID Used in API Call

Buggy Code

fetch(`/api/user/${useId()}`);

What’s Wrong

ID changes per render

Why

useId is not persistent

Fix

Use real identifier:
fetch(`/api/user/${userId}`);

Best Practice

useId is UI-only

8. ❌ ID Changes Break Tests

Buggy Code

expect(screen.getByLabelText("Email")).toMatchSnapshot();

Issue

Snapshot fails due to ID changes

Why

IDs tied to tree structure

Fix

Mock:
jest.mock("react", () => ({
  ...jest.requireActual("react"),
  useId: () => "fixed-id"
}));

Best Practice

Stabilize IDs in tests

9. ❌ Hidden Accessibility Bug

Buggy Code

<label>Email</label>
<input id={useId()} />

What’s Wrong

Label not linked

Why

Missing htmlFor

Fix

const id = useId();
<label htmlFor={id}>Email</label>
<input id={id} />

Best Practice

Always link label + input

10. ❌ Duplicate IDs via Copy-Paste

Buggy Code

const id = "email-input";
Used in multiple components

What’s Wrong

Duplicate DOM IDs

Why

Manual IDs not unique

Fix

const id = useId();

Best Practice

Avoid hardcoded IDs in reusable components

11. ❌ Incorrect Portal Assumption

Buggy Code

createPortal(<Tooltip id={useId()} />, document.body);

What’s Wrong

Hook used outside component

Why

Hooks must be called inside component body

Fix

function TooltipWrapper() {
  const id = useId();
  return createPortal(<Tooltip id={id} />, document.body);
}

Best Practice

Hooks must stay in React render flow

12. ❌ Styling via useId

Buggy Code

#\:r1\:\ { color: red; }

What’s Wrong

IDs unpredictable

Why

React generates opaque IDs

Fix

Use class:
<input className="input" />

Best Practice

Never use useId for styling

13. ❌ Multiple Roots Collision

Buggy Code

const id = useId();
Two apps on same page

What’s Wrong

Potential collisions

Fix

const id = `app1-${useId()}`;

Best Practice

Namespace IDs in multi-root setups

14. ❌ Derived ID Recomputed Incorrectly

Buggy Code

const id = useId();
const derived = computeExpensive(id);

What’s Wrong

Recomputed every render

Why

No memoization

Fix

const id = useId();
const derived = useMemo(() => computeExpensive(id), [id]);

Best Practice

Memoize expensive computations

15. ❌ Incorrect ARIA Linking

Buggy Code

<input aria-labelledby="label-id" />
But:
<label id={useId()}>Email</label>

What’s Wrong

IDs don’t match

Fix

const id = useId();
<label id={`${id}-label`} />
<input aria-labelledby={`${id}-label`} />

Best Practice

Keep ID relationships consistent

16. ❌ Misuse in Custom Hook

Buggy Code

function useField(showHint) {
  if (showHint) {
    return useId();
  }
}

What’s Wrong

Conditional hook

Fix

function useField() {
  return useId();
}

Best Practice

Custom hooks must follow hook rules

17. ❌ Re-render Assumption

Buggy Code

const id = useId();
console.log("New ID:", id);
Dev expects new ID each render

What’s Wrong

Misunderstanding behavior

Why

ID is stable across renders

Fix

No change needed — fix mental model

Best Practice

useId is stable, not dynamic

18. ❌ Breaking Accessibility in Refactor

Buggy Code

<Input />
Internally:
<input id={useId()} />
But label moved outside

What’s Wrong

Label cannot access ID

Fix

Expose ID:
<Input id="email" />
<label htmlFor="email" />

Best Practice

Allow external control of ID

19. ❌ Overusing useId

Buggy Code

const id1 = useId();
const id2 = useId();
const id3 = useId();

What’s Wrong

Unnecessary complexity

Fix

const base = useId();

Best Practice

Prefer single source of truth

20. ❌ Mixing Server-Only Logic

Buggy Code

const id = typeof window === "undefined"
  ? "server-id"
  : useId();

What’s Wrong

Mismatch between server/client

Fix

const id = useId();

Best Practice

Avoid environment-based ID logic

Final Takeaway

These bugs reveal a deeper truth:
useId is not about generating IDs — it’s about preserving deterministic identity across renders, environments, and concurrency.

Below is a senior frontend architect–level machine coding set for useId in React. These are production-grade problems focusing on accessibility, SSR safety, composition, and system design.

1. Accessible Form System (Design System Level)

Requirements

Build a reusable Form System:
  • <FormField>
  • <Label>
  • <Input>
  • <ErrorMessage>
  • <Hint>

UI Behavior

  • Label clicks focus input
  • Error + hint properly announced via screen readers

Data Flow

  • Parent passes validation state
  • Children consume via context

Edge Cases

  • Multiple fields
  • Dynamic validation updates
  • No label case

Performance

  • Avoid unnecessary re-renders in large forms

Architecture

  • Context-based field system
  • useId for base ID
  • Derived IDs for sub-elements

Solution Approach

  1. useFormField() → generates base ID
  2. Context shares IDs
  3. Children attach:
    • htmlFor
    • aria-describedby

2. Dynamic Form Builder (Drag-and-Drop)

Requirements

  • Add/remove/reorder fields
  • Each field accessible

UI Behavior

  • Reordering does not break label-input linking

Data Flow

  • Field list stored in state
  • Each field has persistent data ID

Edge Cases

  • Removing middle field
  • Duplicate labels

Performance

  • Virtualization for large forms

Architecture

  • Field component owns useId
  • Parent manages keys

Solution Steps

  • Never use useId in .map()
  • Encapsulate per-field logic

3. Modal System with Accessibility

Requirements

  • Modal with title, description
  • ARIA compliant

UI Behavior

  • Screen reader announces modal content

Data Flow

  • Controlled open/close state

Edge Cases

  • Multiple modals
  • Nested modals

Performance

  • Avoid unnecessary re-renders

Architecture

  • useId for:
    • aria-labelledby
    • aria-describedby

Solution

  • Generate base ID per modal instance
  • Attach derived IDs

4. Tooltip + Popover System

Requirements

  • Tooltip appears on hover/focus
  • Must support keyboard navigation

UI Behavior

  • Tooltip linked to trigger

Edge Cases

  • Multiple tooltips
  • Nested triggers

Performance

  • Debounced hover events

Architecture

  • Trigger + content pattern
  • useId for linkage

Solution Steps

  • Trigger: aria-describedby
  • Tooltip: role="tooltip"

5. Accessible Tabs Component

Requirements

  • Keyboard navigation (arrow keys)
  • ARIA roles

UI Behavior

  • Active tab controls panel visibility

Edge Cases

  • Dynamic tab addition/removal

Architecture

  • Each tab uses useId

Solution

  • tabId + panelId
  • Link via:
    • aria-controls
    • aria-labelledby

6. Accordion System

Requirements

  • Expand/collapse sections
  • Accessible

UI Behavior

  • Header toggles panel

Edge Cases

  • Multiple expanded panels

Architecture

  • Each item owns useId

Solution

  • Link header + panel via ID

7. Multi-Step Form Wizard

Requirements

  • Steps with validation
  • Navigation between steps

UI Behavior

  • Current step announced to screen readers

Edge Cases

  • Jumping steps
  • Validation errors

Architecture

  • Step components use useId

Solution

  • Each step has unique ARIA region

8. Notification System (ARIA Live)

Requirements

  • Toast messages
  • Screen reader announcements

UI Behavior

  • Messages read aloud

Edge Cases

  • Rapid updates

Architecture

  • Live region container
  • useId for referencing messages

9. Complex Search Input with Suggestions

Requirements

  • Autocomplete dropdown
  • Keyboard navigation

UI Behavior

  • Input linked to suggestion list

Edge Cases

  • Empty results
  • Async loading

Architecture

  • Input + listbox pattern

Solution

  • aria-controls, aria-activedescendant
  • IDs generated via useId

10. Data Table with Row Selection

Requirements

  • Selectable rows
  • Accessible labels

UI Behavior

  • Checkbox per row

Edge Cases

  • Virtualized rows

Architecture

  • Row component uses useId

Solution

  • Label linked to checkbox

11. Nested Form Sections

Requirements

  • Sections with sub-fields

UI Behavior

  • Section label applies to group

Edge Cases

  • Deep nesting

Architecture

  • Hierarchical ID generation

Solution

  • Parent ID prefix + child suffix

12. File Upload Component

Requirements

  • Custom UI for file input

UI Behavior

  • Clicking label opens file picker

Edge Cases

  • Multiple uploads

Solution

  • Hidden input + label
  • Linked via useId

13. Settings Panel with Toggles

Requirements

  • Toggle switches with labels

UI Behavior

  • Clicking label toggles switch

Architecture

  • Each toggle uses useId

14. Chat Input with Accessibility

Requirements

  • Input + send button
  • Announcements for messages

Solution

  • useId for input linking and live region

15. Reusable Field Wrapper System

Requirements

  • Wrap arbitrary inputs
  • Inject accessibility props

Architecture

  • Clone children
  • Inject ID from useId

16. Multi-Root Widget System

Requirements

  • Multiple widgets on same page

Edge Cases

  • ID collisions

Solution

  • Namespace IDs:
const id = `widget-${useId()}`;

17. SSR Streaming Page (Next.js-like)

Requirements

  • Server-rendered page
  • Streaming components

Edge Cases

  • Suspense boundaries

Architecture

  • All components use useId

Insight

  • Ensures hydration consistency

18. Custom Hook Library (useAccessibleField)

Requirements

  • Reusable hook for fields

Output

{
  inputId,
  labelId,
  hintId
}

Architecture

  • Base ID from useId
  • Derived IDs

19. Drag-and-Drop Accessible List

Requirements

  • Reorderable list
  • Screen reader announcements

Edge Cases

  • Keyboard drag

Architecture

  • Item-level useId

20. Design System Input with Validation + Analytics

Requirements

  • Input with:
    • validation
    • analytics tracking

Edge Cases

  • Dynamic validation messages

Architecture

  • useId for accessibility
  • Separate tracking ID (not useId)

Insight

Separate UI identity from analytics identity

Final Architectural Insight

At scale, useId becomes:

❌ NOT:

  • Just an ID generator

✅ BUT:

  • A core primitive for accessibility systems
  • A guarantee of SSR-safe identity
  • A foundation for reusable component libraries

Below is a senior-level interview question set on useId in React. These questions are designed to test deep reasoning, system thinking, and real-world trade-offs.

1. Why does useId exist when we already have UUID libraries?

Follow-up

  • When would you still choose UUID over useId?

Strong Answer

useId solves deterministic ID generation for SSR + hydration, which UUIDs cannot. UUIDs generate different values on server and client, causing mismatches. However, UUIDs are better for persistent or business-level identity.

Weak Answer

“It generates unique IDs more easily.” ❌ Fails because it ignores SSR and determinism.

2. Explain how useId works internally in React.

Follow-up

  • What happens if the component tree changes?

Strong Answer

IDs are derived from fiber tree position and hook call order, not from runtime randomness. Any structural change alters ID generation.

Weak Answer

“React keeps a counter.” ❌ Wrong — counters break in concurrent rendering.

3. Why is useId safe in concurrent rendering?

Follow-up

  • What would break if React used a global counter?

Strong Answer

Because it’s pure and deterministic, not dependent on execution order. Concurrent rendering can restart, but useId produces the same output.

Weak Answer

“It just works in React 18.” ❌ No understanding of concurrency model.

4. You see a hydration mismatch even after using useId. What’s your debugging approach?

Follow-up

  • What patterns usually cause this?

Strong Answer

Check for:
  • Conditional rendering differences
  • Environment-based logic (window, typeof)
  • Suspense boundaries
  • Feature flags

Weak Answer

“Check if useId is used correctly.” ❌ Too vague, no debugging strategy.

5. Why is using useId as a list key a bad idea?

Follow-up

  • What real-world bug could this cause?

Strong Answer

Keys must represent data identity, but useId depends on structure → leads to remounting, lost state, and performance issues.

Weak Answer

“Because React docs say so.” ❌ No reasoning.

6. How would you design a reusable input component using useId?

Follow-up

  • How do you handle external control of IDs?

Strong Answer

  • Generate ID via useId
  • Allow override via props.id
  • Use derived IDs for accessibility

Weak Answer

“Just use useId inside the component.” ❌ Ignores flexibility and integration.

7. What trade-offs does useId introduce?

Follow-up

  • When can it become problematic?

Strong Answer

  • IDs tied to structure → fragile under refactoring
  • Not persistent → unsuitable for business logic
  • Harder debugging due to opaque values

Weak Answer

“No major trade-offs.” ❌ Unrealistic.

8. How does useId improve accessibility?

Follow-up

  • Give a real-world accessibility failure without it

Strong Answer

Enables reliable linking:
  • labelinput
  • aria-describedby Without it → broken screen reader relationships

Weak Answer

“It helps with labels.” ❌ Too shallow.

9. What happens if you call useId conditionally?

Follow-up

  • Why doesn’t React throw immediately?

Strong Answer

Breaks hook order → incorrect ID assignment React doesn’t always throw → leads to subtle bugs

Weak Answer

“It throws an error.” ❌ Not always true.

10. How would you design a form system using useId at scale?

Follow-up

  • How do you avoid prop drilling?

Strong Answer

  • Use context to share IDs
  • Base ID per field
  • Derived IDs for children

Weak Answer

“Pass ID as props.” ❌ Doesn’t scale.

11. Why is useId not suitable for analytics tracking?

Follow-up

  • What would you use instead?

Strong Answer

IDs change with structure → analytics becomes inconsistent Use stable identifiers (UUIDs, backend IDs)

Weak Answer

“It’s not recommended.” ❌ No reasoning.

12. How does useId behave in Suspense/streaming SSR?

Follow-up

  • Why is this important for modern apps?

Strong Answer

React reserves ID slots → ensures consistency even with delayed rendering

Weak Answer

“It works fine with Suspense.” ❌ No depth.

13. You refactor a component and IDs change, breaking tests. What do you do?

Follow-up

  • How do you prevent this in future?

Strong Answer

  • Allow ID overrides
  • Mock useId in tests
  • Avoid relying on IDs in snapshots

Weak Answer

“Update snapshots.” ❌ Ignores root problem.

14. Can useId guarantee uniqueness across multiple React apps on the same page?

Follow-up

  • How would you fix collisions?

Strong Answer

No. Namespace manually using prefixes.

Weak Answer

“Yes, it guarantees uniqueness.” ❌ Incorrect.

15. When should you NOT use useId?

Follow-up

  • What’s the alternative?

Strong Answer

  • Keys
  • API identifiers
  • Styling Use:
  • Data IDs
  • UUIDs
  • Class names

Weak Answer

“Rarely needed.” ❌ Misunderstanding.

16. How would you debug incorrect ARIA relationships in a large app?

Follow-up

  • What tools would you use?

Strong Answer

  • Inspect DOM IDs
  • Check aria-* attributes
  • Use accessibility tools (Lighthouse, screen readers)

Weak Answer

“Check code manually.” ❌ Not scalable.

17. What is the performance impact of useId?

Follow-up

  • Can it cause re-renders?

Strong Answer

  • Minimal overhead
  • Does not trigger re-renders
  • Issues arise only with misuse (e.g., derived heavy computation)

Weak Answer

“It slows down rendering.” ❌ Incorrect.

18. How would you design a custom hook around useId?

Follow-up

  • What should it return?

Strong Answer

Encapsulate logic:
const base = useId();
return {
  inputId: `${base}-input`,
  hintId: `${base}-hint`
};

Weak Answer

“Just return useId().” ❌ Misses abstraction value.

19. What happens if you mix useId with random values?

Follow-up

  • Why is this dangerous?

Strong Answer

Breaks determinism → hydration mismatch

Weak Answer

“It still works.” ❌ Incorrect.

20. How would you explain useId to a junior engineer?

Follow-up

  • What mental model would you give?

Strong Answer

“It’s a deterministic ID tied to render structure, designed for SSR-safe accessibility.”

Weak Answer

“It generates unique IDs.” ❌ Too simplistic.

21. Design a component library — where would useId fit?

Follow-up

  • What patterns would you standardize?

Strong Answer

  • Core primitive for accessibility
  • Used in all form controls, overlays, ARIA patterns
  • Standardize ID derivation patterns

Weak Answer

“Only used in inputs.” ❌ Too narrow.

Final Interview Insight

A strong candidate understands:

❌ Not just:

  • “useId generates IDs”

✅ But:

  • Deterministic rendering
  • SSR constraints
  • Accessibility architecture
  • Trade-offs and limitations
  • Separation of concerns (UI vs data identity)