How to generate unique IDs for form labels in React?

Reactjs

Reactjs Problem Overview


I have form elements with labels and I want to have unique IDs to link labels to elements with htmlFor attribute. Something like this:

React.createClass({
	render() {
		const id = ???;
		return (
			<label htmlFor={id}>My label</label>
			<input id={id} type="text"/>
		);
	}
});

I used to generate IDs based on this._rootNodeID but it’s unavailable since React 0.13. What is the best and/or simplest way to do it now?

Reactjs Solutions


Solution 1 - Reactjs

The id should be placed inside of componentWillMount (update for 2018) constructor, not render. Putting it in render will re-generate new ids unnecessarily.

If you're using underscore or lodash, there is a uniqueId function, so your resulting code should be something like:

constructor(props) {
    super(props);
    this.id = _.uniqueId("prefix-");
}

render() { 
  const id = this.id;
  return (
    <div>
        <input id={id} type="checkbox" />
        <label htmlFor={id}>label</label>
    </div>
  );
}

2019 Hooks update:

import React, { useState } from 'react';
import _uniqueId from 'lodash/uniqueId';

const MyComponent = (props) => {
  // id will be set once when the component initially renders, but never again
  // (unless you assigned and called the second argument of the tuple)
  const [id] = useState(_uniqueId('prefix-'));
  return (
    <div>
      <input id={id} type="checkbox" />
      <label htmlFor={id}>label</label>
    </div>
  );
}

Solution 2 - Reactjs

This solutions works fine for me.

utils/newid.js:

let lastId = 0;

export default function(prefix='id') {
    lastId++;
	return `${prefix}${lastId}`;
}

And I can use it like this:

import newId from '../utils/newid';

React.createClass({
    componentWillMount() {
        this.id = newId();
    },
    render() {
        return (
            <label htmlFor={this.id}>My label</label>
            <input id={this.id} type="text"/>
        );
    }
});

But it won’t work in isomorphic apps.

Added 17.08.2015. Instead of custom newId function you can use uniqueId from lodash.

Updated 28.01.2016. It’s better to generate ID in componentWillMount.

Solution 3 - Reactjs

Following up as of 2019-04-04, this seems to be able to be accomplished with the React Hooks' useState:

import React, { useState } from 'react'
import uniqueId from 'lodash/utility/uniqueId'

const Field = props => {
  const [ id ] = useState(uniqueId('myprefix-'))

  return (
    <div>
      <label htmlFor={id}>{props.label}</label>
      <input id={id} type="text"/>
    </div>
  )      
}

export default Field

As I understand it, you ignore the second array item in the array destructuring that would allow you to update id, and now you've got a value that won't be updated again for the life of the component.

The value of id will be myprefix-<n> where <n> is an incremental integer value returned from uniqueId. If that's not unique enough for you, consider making your own like

function gen4() {
  return Math.random().toString(16).slice(-4)
}

function simpleUniqueId(prefix) {
  return (prefix || '').concat([
    gen4(),
    gen4(),
    gen4(),
    gen4(),
    gen4(),
    gen4(),
    gen4(),
    gen4()
  ].join(''))
}

or check out the library I published with this here: https://github.com/rpearce/simple-uniqueid. There are also hundreds or thousands of other unique ID things out there, but lodash's uniqueId with a prefix should be enough to get the job done.


Update 2019-07-10

Thanks to @Huong Hk for pointing me to hooks lazy initial state, the sum of which is that you can pass a function to useState that will only be run on the initial mount.

// before
const [ id ] = useState(uniqueId('myprefix-'))

// after
const [ id ] = useState(() => uniqueId('myprefix-'))

Solution 4 - Reactjs

Update React 18

React 18 has introduced a new hook which generates a unique ID:

const id = useId();

Hook API docs: https://reactjs.org/docs/hooks-reference.html#useid

From your example, you could call the hook inside a component:

import React, { useId } from 'react'

function TextField = (props) => {
  // generate unique ID
  const id = useId(); 

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

Solution 5 - Reactjs

You could use a library such as node-uuid for this to make sure you get unique ids.

Install using:

npm install node-uuid --save

Then in your react component add the following:

import {default as UUID} from "node-uuid";
import {default as React} from "react";

export default class MyComponent extends React.Component {   
  componentWillMount() {
    this.id = UUID.v4();
  }, 
  render() {
    return (
      <div>
        <label htmlFor={this.id}>My label</label>
        <input id={this.id} type="text"/>
      </div>
    );
  }   
}

Solution 6 - Reactjs

Extending @forivall's comment

If the whole goal is to link up a <label> and <input> elements and they don't depend on props, then instead of using auto generated unique id's, the most optimal and performant approach would be to use useRef.

> useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.

Meaning, you can use useRef to mimic instance variables which is not recomputed on props changes. useRef is not only used to reference a DOM element.

Example using an external random ID generator (e.g. loadash)
import React, { useRef } from 'react'
import uniqueId from 'lodash/utility/uniqueId'

function InputField = (props) => {
  const {current: fieldId} = useRef(uniqueId('prefix-'))
  return (
    <div>
      <input id={fieldId} type="checkbox" />
      <label htmlFor={fieldId}>label</label>
    </div>
  );
}
Example using a simple custom random ID generator
import React, { useRef } from 'react'

function InputField = (props) => {
  const {current: fieldId} = useRef("prefix-" + (Math.random().toString(36)+'00000000000000000').slice(2, 7))
  return (
    <div>
      <input id={fieldId} type="checkbox" />
      <label htmlFor={fieldId}>label</label>
    </div>
  );
}

Explanation:

The above random ID (Math.random().toString(36)+'00000000000000000').slice(2, 7) comes from this stackoverflow answer and will always guarantee 5 characters, compared to Math.random().toString(16).slice(-4) which may return empty strings.

Also, it's important to use a prefix where the prefix must start with a letter ([A-Za-z]) in order for it to be a valid HTML4 id attribute value.

Solution 7 - Reactjs

Hopefully this is helpful to anyone coming looking for a universal/isomorphic solution, since the checksum issue is what led me here in the first place.

As said above, I've created a simple utility to sequentially create a new id. Since the IDs keep incrementing on the server, and start over from 0 in the client, I decided to reset the increment each the SSR starts.

// utility to generate ids
let current = 0

export default function generateId (prefix) {
  return `${prefix || 'id'}-${current++}`
}

export function resetIdCounter () { current = 0 }

And then in the root component's constructor or componentWillMount, call the reset. This essentially resets the JS scope for the server in each server render. In the client it doesn't (and shouldn't) have any effect.

Solution 8 - Reactjs

A version without Lodash using hooks:

function useUniqueId() {
  const [id] = useState(() => `component-${Math.random().toString(16).slice(2)}`)
  return id
}

Solution 9 - Reactjs

I create a uniqueId generator module (Typescript):

const uniqueId = ((): ((prefix: string) => string) => {
  let counter = 0;
  return (prefix: string): string => `${prefix}${++counter}`;
})();

export default uniqueId;

And use top module to generate unique ids:

import React, { FC, ReactElement } from 'react'
import uniqueId from '../../modules/uniqueId';

const Component: FC = (): ReactElement => {
  const [inputId] = useState(uniqueId('input-'));
  return (
    <label htmlFor={inputId}>
      <span>text</span>
      <input id={inputId} type="text" />
    </label>
  );
};     

Solution 10 - Reactjs

For the usual usages of label and input, it's just easier to wrap input into a label like this:

import React from 'react'

const Field = props => (
  <label>
    <span>{props.label}</span>
    <input type="text"/>
  </label>
)      

It's also makes it possible in checkboxes/radiobuttons to apply padding to root element and still getting feedback of click on input.

Solution 11 - Reactjs

The useId hook will replace the unstable useOpaqueIdentifier in an upcoming stable version of React (already available in the latest React alphas). It will generate stable ids during server rendering and hydration to avoid mismatches.

Solution 12 - Reactjs

I found an easy solution like this:

class ToggleSwitch extends Component {
  static id;

  constructor(props) {
    super(props);

    if (typeof ToggleSwitch.id === 'undefined') {
      ToggleSwitch.id = 0;
    } else {
      ToggleSwitch.id += 1;
    }
    this.id = ToggleSwitch.id;
  }

  render() {
    return (
        <input id={`prefix-${this.id}`} />
    );
  }
}

Solution 13 - Reactjs

Don't use IDs at all if you don't need to, instead wrap the input in a label like this:

<label>
   My Label
   <input type="text"/>
</label>

Then you won't need to worry about unique IDs.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionArtem SapeginView Question on Stackoverflow
Solution 1 - ReactjsKabir SarinView Answer on Stackoverflow
Solution 2 - ReactjsArtem SapeginView Answer on Stackoverflow
Solution 3 - ReactjsrpearceView Answer on Stackoverflow
Solution 4 - Reactjst_dom93View Answer on Stackoverflow
Solution 5 - ReactjsStuart ErvineView Answer on Stackoverflow
Solution 6 - ReactjsTanckomView Answer on Stackoverflow
Solution 7 - Reactjstenor528View Answer on Stackoverflow
Solution 8 - ReactjsJoonasView Answer on Stackoverflow
Solution 9 - ReactjsMasih JahangiriView Answer on Stackoverflow
Solution 10 - ReactjsDevelopiaView Answer on Stackoverflow
Solution 11 - ReactjsLucky SoniView Answer on Stackoverflow
Solution 12 - ReactjsOZZIEView Answer on Stackoverflow
Solution 13 - ReactjsMike DesjardinsView Answer on Stackoverflow