dotpegasopt
Written by Andre Benatti ยท 2 min read

Solving non-unique ID error for shared components in React

If you go over the design documents for developers provided by The Chromium Projects, you're going to face the following advice under the Create Amazing Password Forms section:

Web browsers are designed with the HTML specification in mind, and going against it can lead to unexpected issues with your web page. This means:

Element id attributes should be unique: no two elements should have the same id.

But it's way too common to rely on shared components having the same ID across different parts of the application, due to multiple instances of it being rendered on the same page.

The most common one as an example: The LoginForm component:

// <LoginForm /> component:
function LoginForm() => (
  <form>
    ...
    <label htmlFor="username">
      Username
    </label>
    <input
      id="username"
      type="text"
      name="username"
      placeholder="Username"
    />
    ...
  </form>
);
...
// Homepage
<>
  <header>
    <LoginForm /> {/* first instance */}
  </header>
  <main>
  ...
  </main>
  <footer>
    <LoginForm /> {/* second instance */}
  </footer>
</>

You can see that both instances of the LoginForm component would have the same ID (username), which is not allowed according to the HTML specification.

And I know many could think we can solve this issue by passing a unique id prop to each instance of the component, but that's not the right approach.

Behold: React.useId() hook

React provides a useId hook that generates a unique ID for each instance of a component.

import { useId } from 'react';
 
function LoginForm() {
  const id = useId();
  const usernameId = `${id}-username`;
 
  return (
    <form>
      <label htmlFor={usernameId}>
        Username
      </label>
      <input
        id={usernameId}
        type="text"
        name="username"
        placeholder="Username"
      />
    </form>
  );
}

If you console.log the id value, it would look something like this:

React.useId() example

And that value persists across re-renders, which is also better than passing props or other solutions like Math.random().

See you next time! ๐Ÿ‘‹๐Ÿพ