Telerik blogs

React’s useId hook offers an accessible solution for managing unique identifiers in components that are consisten between server and client.

In React version 18, React introduced a new hook titled useId that provides a very helpful solution for managing unique identifiers within components. In this article, we’ll spend some time exploring its functionality, use cases and benefits in creating more accessible and maintainable React applications.

useId Hook

In a nutshell, the useId hook generates a unique and stable ID for components. This ID remains consistent across server and client renders, ensuring that server-side generated HTML matches the client-side generated HTML. This is particularly useful for accessibility concerns like associating form inputs with labels or in any scenario where unique identifiers are needed within a component.

Let’s walk through an example to illustrate the use of the useId hook in React. We’ll start with a simple component, say, a form component with an input field and a label.

import React from "react";

export function Form() {
  return (
    <div>
      <label htmlFor="nameInput">Name:</label>
      <input id="nameInput" type="text" />
    </div>
  );
}

In the above code example, we’re hardcoding the id for the input and the corresponding htmlFor attribute for the label. This works fine for a single instance of FormComponent, but if we were to use multiple instances of this component on the same page, we’d encounter duplicate ID issues, which isn’t ideal for accessibility and can cause HTML validation issues.

If we were to use multiple instances of the above Form component on the same page, we would want to dynamically generate a unique ID for each instance of the input field and its associated label. This is crucial when we have multiple instances of the component, as each input-label pair needs a unique identifier.

To achieve this goal, we’ll refactor the Form component to use the useId hook to generate a unique ID:

import React, { useId } from "react";

export function Form() {
  // Generate a unique ID
  const inputId = useId();

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

In this revised version, the useId hook generates a unique ID, stored in a variable named inputId, which we then use for the id attribute of the input and the htmlFor attribute of the label.

With these recent changes, suppose we have a parent App component that renders multiple Form components.

import React from "react";
import { Form } from "./Form";

function App() {
  return (
    <div>
      <Form />
      <Form />
      <Form />
    </div>
  );
}

When React renders these components, each Form instance calls useId, which generates a unique ID. The first instance might generate an ID like "input-0", the second "input-1" and the third "input-2". The actual values depend on React’s internal mechanism and may vary. For example, when testing the above example, we notice unique IDs like the following:

Three name form fields on the left, and the inspect tool showing the code with ids r1, r3 and r5

See the above running code example in this CodeSandbox link.

At any point in time, if we need to add custom prefixes within the component to further distinguish the IDs generated by the useId hook, we can create a simple wrapper function around useId. This approach can be useful in large applications where different modules or components might need more specific ID management to avoid potential clashes, even with the unique IDs generated by useId.

Here’s an example of how we could implement a custom prefix within the component by creating a custom usePrefixedId hook:

import React, { useId } from "react";

// A wrapper function to add a custom prefix to the ID
function usePrefixedId(prefix) {
  const id = useId();
  return `${prefix}-${id}`;
}

export function Form({ prefix }) {
  // Use the custom hook to generate a prefixed ID
  const inputId = usePrefixedId(prefix);

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

In the above example, the custom usePrefixedId hook takes a prefix and appends it to the ID generated by useId. When using the Form component, we can optionally pass a prefix prop to customize the ID. If no prefix is provided, it defaults to 'form'.

In the parent App component instance we had earlier, we can specify different prefixes for each component like the following:

import React from "react";
import { Form } from "./Form";

function App() {
  return (
    <div>
      <Form prefix="user" />
      <Form prefix="profile" />
      <Form prefix="contact" />
    </div>
  );
}

The IDs for each Form component will not just be unique across instances but also clearly associated with a specific context or part of the application (e.g., "user-input-0", "profile-input-1" and "contact-input-2"). The actual values still depend on React’s internal ID generation mechanism but will now include the specified prefixes, adding an additional layer of clarity and distinction.

Three name form fields on the left, and the inspect tool showing the code with ids user-r1, profile-r3 and contact-r5

See the above running code example in this CodeSandbox link.

Lastly, the React documentation tells us we’re also able to customize the prefix of all identifiers generated by the useId hook by using the identifierPrefix option in the createRoot call that is used to initialize the React application:

import { createRoot } from "react-dom/client";
import App from "./App";

const rootElement = document.getElementById("root", {
  // Set a custom prefix for identifiers generated by useId
  identifierPrefix: "fresh-app-",
});

const root = createRoot(rootElement);
root.render(<App />);

Specifying a global prefix like the above is often helpful when rendering multiple React applications on a single page. It helps to avoid ID conflicts between components from different applications. Each application can have its own unique prefix, so that IDs generated by useId are unique not only within each application but across all applications on the page.

Wrap-up

In summary, React’s useId hook offers an accessible solution for managing unique identifiers in components. Dynamically generating stable and consistent IDs helps to keep the identifiers consistent across server and client renders. This consistency is crucial for applications that rely on server-side rendering, as it guarantees that the server-rendered HTML matches the client-rendered HTML.

For more details on the useId hook, be sure to read through the official React documentation.


About the Author

Hassan Djirdeh

Hassan is a senior frontend engineer and has helped build large production applications at-scale at organizations like Doordash, Instacart and Shopify. Hassan is also a published author and course instructor where he’s helped thousands of students learn in-depth frontend engineering skills like React, Vue, TypeScript, and GraphQL.

Related Posts

Comments

Comments are disabled in preview mode.