useIdLink for this heading

January 6, 2025 · View on GitHub

useId is a React Hook for generating unique IDs that can be passed to accessibility attributes.

const id = useId()

Call useId at the top level of your component to generate a unique ID:

import { useId } from 'react';

function PasswordField() {

  const passwordHintId = useId();

  // ...

See more examples below.

useId does not take any parameters.

useId returns a unique ID string associated with this particular useId call in this particular component.

  • useId is a Hook, so you can only call it at the top level of your component or your own Hooks. You can’t call it inside loops or conditions. If you need that, extract a new component and move the state into it.
  • useId should not be used to generate keys in a list. Keys should be generated from your data.

Pitfall

Do not call useId to generate keys in a list. Keys should be generated from your data.

Call useId at the top level of your component to generate a unique ID:

import { useId } from 'react';

function PasswordField() {

  const passwordHintId = useId();

  // ...

You can then pass the generated ID to different attributes:

<>

  <input type="password" aria-describedby={passwordHintId} />

  <p id={passwordHintId}>

</>

Let’s walk through an example to see when this is useful.

HTML accessibility attributes like aria-describedby let you specify that two tags are related to each other. For example, you can specify that an element (like an input) is described by another element (like a paragraph).

In regular HTML, you would write it like this:

<label>

  Password:

  <input

    type="password"

    aria-describedby="password-hint"

  />

</label>

<p id="password-hint">

  The password should contain at least 18 characters

</p>

However, hardcoding IDs like this is not a good practice in React. A component may be rendered more than once on the page—but IDs have to be unique! Instead of hardcoding an ID, generate a unique ID with useId:

import { useId } from 'react';

function PasswordField() {

  const passwordHintId = useId();

  return (

    <>

      <label>

        Password:

        <input

          type="password"

          aria-describedby={passwordHintId}

        />

      </label>

      <p id={passwordHintId}>

        The password should contain at least 18 characters

      </p>

    </>

  );

}

Now, even if PasswordField appears multiple times on the screen, the generated IDs won’t clash.

App.js

App.js

ResetFork

import { useId } from 'react';

function PasswordField() {
  const passwordHintId = useId();
  return (
    <>
      <label>
        Password:
        <input
          type="password"
          aria-describedby={passwordHintId}
        />
      </label>
      <p id={passwordHintId}>
        The password should contain at least 18 characters
      </p>
    </>
  );
}

export default function App() {
  return (
    <>
      <h2>Choose password</h2>
      <PasswordField />
      <h2>Confirm password</h2>
      <PasswordField />
    </>
  );
}

Show more

Watch this video to see the difference in the user experience with assistive technologies.

Pitfall

With server rendering, useId requires an identical component tree on the server and the client. If the trees you render on the server and the client don’t match exactly, the generated IDs won’t match.

Deep Dive

Show Details

You might be wondering why useId is better than incrementing a global variable like nextId++.

The primary benefit of useId is that React ensures that it works with server rendering. During server rendering, your components generate HTML output. Later, on the client, hydration attaches your event handlers to the generated HTML. For hydration to work, the client output must match the server HTML.

This is very difficult to guarantee with an incrementing counter because the order in which the Client Components are hydrated may not match the order in which the server HTML was emitted. By calling useId, you ensure that hydration will work, and the output will match between the server and the client.

Inside React, useId is generated from the “parent path” of the calling component. This is why, if the client and the server tree are the same, the “parent path” will match up regardless of rendering order.


If you need to give IDs to multiple related elements, you can call useId to generate a shared prefix for them:

App.js

App.js

ResetFork

import { useId } from 'react';

export default function Form() {
  const id = useId();
  return (
    <form>
      <label htmlFor={id + '-firstName'}>First Name:</label>
      <input id={id + '-firstName'} type="text" />
      <hr />
      <label htmlFor={id + '-lastName'}>Last Name:</label>
      <input id={id + '-lastName'} type="text" />
    </form>
  );
}

This lets you avoid calling useId for every single element that needs a unique ID.


If you render multiple independent React applications on a single page, pass identifierPrefix as an option to your createRoot or hydrateRoot calls. This ensures that the IDs generated by the two different apps never clash because every identifier generated with useId will start with the distinct prefix you’ve specified.

index.jsindex.htmlApp.js

index.js

ResetFork

import { createRoot } from 'react-dom/client';
import App from './App.js';
import './styles.css';

const root1 = createRoot(document.getElementById('root1'), {
  identifierPrefix: 'my-first-app-'
});
root1.render(<App />);

const root2 = createRoot(document.getElementById('root2'), {
  identifierPrefix: 'my-second-app-'
});
root2.render(<App />);


If you render multiple independent React apps on the same page, and some of these apps are server-rendered, make sure that the identifierPrefix you pass to the hydrateRoot call on the client side is the same as the identifierPrefix you pass to the server APIs such as renderToPipeableStream.

// Server

import { renderToPipeableStream } from 'react-dom/server';

const { pipe } = renderToPipeableStream(

  <App />,

  { identifierPrefix: 'react-app1' }

);
// Client

import { hydrateRoot } from 'react-dom/client';

const domNode = document.getElementById('root');

const root = hydrateRoot(

  domNode,

  reactNode,

  { identifierPrefix: 'react-app1' }

);

You do not need to pass identifierPrefix if you only have one React app on the page.

PrevioususeEffect

NextuseImperativeHandle


Copyright © Meta Platforms, Inc

no uwu plz

uwu?

Logo by@sawaratsuki1004

Learn React

Quick Start

Installation

Describing the UI

Adding Interactivity

Managing State

Escape Hatches

API Reference

React APIs

React DOM APIs

Community

Code of Conduct

Meet the Team

Docs Contributors

Acknowledgements

More

Blog

React Native

Privacy

Terms

On this page


React

v19

Search⌘CtrlK

Learn

Reference

Community

Blog

Is this page useful?

API Reference

Hooks