182

I want to create a component with Vue.js containing a label and an input. For example :

<label for="inputId">Label text</label>
<input id="inputId" type="text" />

How can I set a unique ID for each component instance?

Penny Liu
  • 15,447
  • 5
  • 79
  • 98
PierreB
  • 1,947
  • 3
  • 16
  • 16
  • There are several packages/mixins which you can look into: [vue-ucid](https://github.com/troxler/vue-ucid), [vue-component-id](https://github.com/VitorLuizC/vue-component-id), [vue-uniq-ids](https://github.com/termosa/vue-uniq-ids). – str Feb 19 '19 at 10:46
  • 1
    Having not seen the previous comment before, I also published the [vue-unique-id Vue plugin for this on npm](https://www.npmjs.com/package/vue-unique-id). – bernie Mar 04 '19 at 14:34

16 Answers16

235

Each component has a unique id which can be accessed as this._uid.

<template>
  <div>
    <label :for="id">Label text for {{id}}</label>
    <input :id="id" type="text" />
  </div>
</template>

<script>
export default {
  data () {
    return {
      id: null
    }
  }, 
  mounted () {
    this.id = this._uid
  }
}
</script>

If you want more control over the ids you can for example, generate them inside a parent component.

kissu
  • 40,416
  • 14
  • 65
  • 133
zxzak
  • 8,985
  • 4
  • 27
  • 25
  • 1
    Note that the `ready` method was removed in Vue 2.0 and above. I was very confused when the `ready` method wasn't executing. http://stackoverflow.com/a/40209796/126751 – Brendan Wood Feb 08 '17 at 02:35
  • 1
    ... and `data` must be a function that returns an object: https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function – arminrosu Aug 21 '17 at 08:38
  • 5
    Unfortunately, this does not work in TypeScript, as `this._uid` is not valid. Instead, generate your id yourself, e.g. `public id = uuid4();`, see [uuid4](https://stackoverflow.com/a/2117523/319711). – Erik Vullings Nov 23 '17 at 15:19
  • 2
    I had to put the initialization in the beforeMount() method to make sure the id was set in the DOM when I tried to access it from the mounted() method. – Stéphane Gerber Apr 02 '18 at 12:35
  • 89
    Don't use `_uid`, it "*[is reserved for internal use and it's important to keep it private (and not rely on it in user code) so that we keep the flexibility to change its behavior for potential future use cases](https://github.com/vuejs/vue/issues/5886#issuecomment-308625735)*". – str Feb 10 '19 at 11:09
  • For TypeScript add _uid to the interface `declare module 'vue/types/vue' { interface Vue { _uid: any; } }` But don't use it. Also in combination with SSR `_uid` and `uuid4` are no options. – Steve Maris Feb 04 '20 at 13:13
  • `:id="id"` was very useful – Cortex Aug 02 '20 at 02:42
  • 1
    Does not work in vue3 => use __buckthorn__ 's answer below – Edward Gaere Dec 22 '22 at 15:45
72

To Nihat's point (above): Evan You has advised against using _uid: "The vm _uid is reserved for internal use and it's important to keep it private (and not rely on it in user code) so that we keep the flexibility to change its behavior for potential future use cases. ... I'd suggest generating UIDs yourself [using a module, a global mixin, etc.]"

Using the suggested mixin in this GitHub issue to generate the UID seems like a better approach:

let uuid = 0;

export default {
  beforeCreate() {
    this.uuid = uuid.toString();
    uuid += 1;
  },
};
kissu
  • 40,416
  • 14
  • 65
  • 133
buckthorn
  • 1,727
  • 13
  • 15
21

Update

I published the vue-unique-id Vue plugin for this on npm.

Answer

None of the other solutions address the requirement of having more than one form element in your component. Here's my take on a plugin that builds on previously given answers:

Vue.use((Vue) => {
  // Assign a unique id to each component
  let uidCounter = 0;
  Vue.mixin({
    beforeCreate: function() {
      this.uidCounter = uidCounter.toString();
      uidCounter += 1;
    },
  });

  // Generate a component-scoped id
  Vue.prototype.$id = function(id) {
    return "uid-" + this.uidCounter + "-" + id;
  };
});

This doesn't rely on the internal _uid property which is reserved for internal use.

Use it like this in your component:

<label :for="$id('field1')">Field 1</label>
<input :id="$id('field1')" type="text" />

<label :for="$id('field2')">Field 2</label>
<input :id="$id('field2')" type="text" />

To produce something like this:

<label for="uid-42-field1">Field 1</label>
<input id="uid-42-field1" type="text" />

<label for="uid-42-field2">Field 2</label>
<input id="uid-42-field2" type="text" />
bernie
  • 9,820
  • 5
  • 62
  • 92
  • 2
    A note that this plugin does not appear to work for Vue3 – Ethan Z Aug 03 '21 at 04:24
  • 1
    Using uuid +=1 is terrible practice. The sequence (0,1,2,3,...) is problematic enough by itself but naming it uuid is mockery of the UUID concepts.... Use uuid library with uuidv4 and you will be fine. The main advantage (apart from philosophical concepts) is that you do not need any shared storage for the current value and the code will not have artificially created sections begging for race condition issues... – Radek Hladík Mar 10 '22 at 13:50
  • 2
    `uuid` was an unfortunate name choice so I've renamed it to `uidCounter`. Javascript is single threaded so I don't see which race conditions could possibly creep in the code above. `uidCounter` is read and updated atomically because no there are no other threads and execution isn't preempted. Imo, an actual UUID doesn't offer any benefit over a simple incrementing counter for this use-case, but yes it works. – bernie Mar 13 '22 at 09:46
20

Update: Code will throw an error if ._uid property does not exist in the instance so that you can update it to use something custom or new unique id property if provided by Vue.

Although zxzak's answer is great; _uid is not a published api property. To save a headache in case it changes in the future, you can update your code with just one change with a plugin solution like below.

Vue.use({
    install: function(Vue, options) {
        Object.defineProperty(Vue.prototype, "uniqId", {
            get: function uniqId() {
                if ('_uid' in this) {
                   return this._uid;
                }
                throw new Error("_uid property does not exist");
            }
        });
    }
});
kissu
  • 40,416
  • 14
  • 65
  • 133
Nihat
  • 3,055
  • 3
  • 18
  • 28
  • 2
    This is still using the uid, which in your own answer acknowledges is advised against. Please don't post answers advocating bad practices. This answer should be removed. – Hybrid web dev Jul 13 '19 at 04:34
  • 4
    Yes but in case the published api is changed / removed, they will have to change only one place in the entire code. In the other answer, it was per component. I already emphasized that in the title. – Nihat Jul 13 '19 at 13:23
  • Also, I just updated the code so that it will throw an error in case `_uid` property does not exist anymore. – Nihat Jul 13 '19 at 13:35
16
npm i -S lodash.uniqueid

Then in your code...

<script>
  const uniqueId = require('lodash.uniqueid')

  export default {
    data () {
      return {
        id: ''
      }
    },
    mounted () {
       this.id = uniqueId()
    }
  }
</script>

This way you're not loading the entire lodash library, or even saving the entire library to node_modules.

kissu
  • 40,416
  • 14
  • 65
  • 133
m.e.conroy
  • 3,508
  • 25
  • 27
  • lodash.uniqueid is lightweight package and easy to use. (MINIFIED: 857B) – Seyed Abbas Seyedi Jul 04 '22 at 06:33
  • This answer seems incomplete. Don't you have to bind the ID on the component somehow? MDN's tutorial gives a similar answer [here](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_first_component#giving_todos_a_unique_id), but that just generates warnings and no `id` properties on elements. Also, `-S` (or `--save`) has been the default on `npm` since 2017 and doesn't actually do anything any more. – Vince Jul 13 '22 at 11:04
8

For Vue.js v3 you can get id like this:

In template: {{ $.uid }}

In script: this.$.uid

Or use your own function or mix them:

this.componentUid = ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
          (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
        );

This will return e.g:

aa174375-5b75-4919-acd0-980fcd54003c

holesx_x
  • 236
  • 4
  • 4
5

The simplest way I found was to create a UUID (uuid package) manually through a global mixin. That way you won't rely on anything that can potentially change or become deprecated in the future like this._uid.

You first have to install the uuid package:

npm i uuid

Then, in your main.js file create a global mixin:

// rest of imports

import { v4 as uuidv4 } from 'uuid';

const app = Vue.createApp(App);

app.mixin({
    data() {
        return {
            componentId: uuidv4()
        }
    },
});

app.use(store).use(router).mount('#app');

And here is how you can us it in a component:

<template>
   <div>
      <h1>{{ componentId }}</h1>
      <button @click="printId()">click me for componentId.</button>
   </div>
</template>

<script>
export default {
   methods: {
      printId: function() {
         console.log(this.componentId);
      }
   }
}
</script>
get_php_workin
  • 438
  • 7
  • 14
4

This seem to work for me using in nuxtjs

https://www.npmjs.com/package/uuid

example of generated output: element: 47bfe557-d75f-455c-9a37-85b7935b297b

package.json

"dependencies": {    
    "uuid": "^8.3.2"
 },

on child component, might not be the best way but seem to work

...

<ComponentName v-if="element" />

...

import { v4 as uuidv4 } from 'uuid';

...

data() {
  return {
    element: null,
  }
}

...

mounted() {
  this.element = uuidv4();
}
kissu
  • 40,416
  • 14
  • 65
  • 133
atazmin
  • 4,757
  • 1
  • 32
  • 23
2

In Vue2, use v-bind.

Say I have an object for a poll

<div class="options" v-for="option in poll.body.options">
  <div class="poll-item">
    <label v-bind:for="option._id" v-bind:style="{color: option.color}">
      {{option.text}}
    </label>
    <input type="radio" v-model="picked" v-bind:value="option._id" v-bind:id="option._id">
  </div>
</div>
kissu
  • 40,416
  • 14
  • 65
  • 133
趙曉明
  • 73
  • 5
2

A simple approach that I haven't seen in the replies is:

<template>
  <div>
    <label :for="id">Label text for {{id}}</label>
    <input :id="id" type="text" />
  </div>
</template>

<script>
import uniqueId from 'lodash-es/uniqueId'

export default {
  computed: {
    id () {
      # return this._uid
      return uniqueId('id')
    }
  }
}
</script>
kissu
  • 40,416
  • 14
  • 65
  • 133
Cristi Draghici
  • 518
  • 7
  • 10
  • 3
    The creator of Vue.js says you should avoid using _uid because it's for internal use and someday they may remove it or rename it or change its behaviour. – Omid Sadeghi Sep 08 '19 at 21:19
  • 1
    Thanks, I think that is correct. I have updated the code with a different solution, hopefully still simple enough. Anyway, the idea of this example was to use a computed property. – Cristi Draghici Sep 09 '19 at 22:44
  • uniqueId from lodash is the best approach in my opinion – Giorgio Tempesta Nov 08 '19 at 10:23
2

If you're using TypeScript, without any plugin, you could simply add a static id in your class component and increment it in the created() method. Each component will have a unique id (add a string prefix to avoid collision with another components which use the same tip)

<template>
  <div>
    <label :for="id">Label text for {{id}}</label>
    <input :id="id" type="text" />
  </div>
</template>

<script lang="ts">
  ...
  @Component
  export default class MyComponent extends Vue {
    private id!: string;
    private static componentId = 0;
    ...
    created() {
      MyComponent.componentId += 1;
      this.id = `my-component-${MyComponent.componentId}`;
    }
</script>
kissu
  • 40,416
  • 14
  • 65
  • 133
JMess
  • 39
  • 2
  • What is the equivalent for the non-`class`-based syntax for defining Vue components? For example using `export default defineComponent({ created() { ... }, ... }); – Keavon Jun 12 '21 at 07:03
0

This package seems to be a good solution for the underlying issue of having non-unique IDs in your DOM across multiple components:

vue-uniq-ids

It is a trend to use components. Components are cool, they are small, obvious, easy to use and modular. Untill it comes to the id property.

Some HTML tag attributes requires using an id property, like label[for], input[form] and many of aria-* attributes. And the problem with the id is that it is not modular. If several id properties on the page will has the same value they can affect each other.

VueUniqIds helps you to get rid of this problem. It provides the set of id-related directives which value is automatically modified by adding unique string while keeping the attrbitue easy to read.

Community
  • 1
  • 1
tryforceful
  • 143
  • 1
  • 8
0

This seem to work for me using https://www.npmjs.com/package/uuid

example of generated output: element: 47bfe557-d75f-455c-9a37-85b7935b297b

package.json

"dependencies": {    
    "uuid": "^8.3.2"
 },

component.vue

v-if="element"

...

import { v4 as uuidv4 } from 'uuid';

...

data() {
  return {
    element: null,
  }
}

...

mounted() {
  this.element = uuidv4();
}
kissu
  • 40,416
  • 14
  • 65
  • 133
atazmin
  • 4,757
  • 1
  • 32
  • 23
-2

According to MDN, you can just make an implicit label binding.

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

This way you don't even need to assign an id.

luvejo
  • 347
  • 2
  • 9
  • 1
    In your example, you don't need an `id` property, but this doesn't really answer the question. There are cases in which you can't avoid needing to use an `id`. Also, MDN has a Vue tutorial ([here](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_first_component#giving_todos_a_unique_id)) where they give an non-working example of how to set unique IDs on components... That's what brought me here. – Vince Jul 13 '22 at 11:09
  • This does not relate directly to the issue at hand, but... while implicit ids are technically valid html, [WCAG H44: Using label elements to associate text labels with form controls](https://www.w3.org/TR/2014/NOTE-WCAG20-TECHS-20140916/H44#H44-tests) strongly recommends using the `for` attribute to explicitly associate labels with controls. – buckthorn Dec 23 '22 at 16:37
-7

If your uid is not used by other compoment, I have an idea.

uid: Math.random()

Simple and enough.

YinPeng.Wei
  • 552
  • 3
  • 9
  • 3
    It's difficult to justify this approach when there is a real chance of id collision... – Shadow May 10 '19 at 02:36
  • @Shadow well - with large enough space of possible values it is much better than some "solutions" using +=1. – Radek Hladík Mar 10 '22 at 13:43
  • @RadekHladík The += 1 method is actually going to be far more robust if the id only needs to be unique within the running instance of the application. You'll never get a collision as the page will no doubt be refreshed far before running out of numbers. The random method could have the ids duplicated as soon as the second id, and would also work for far less time due to having access to less numbers... – Shadow Mar 11 '22 at 02:55
  • @shadow I do not want to be offensive but you have no idea what you are talking about. In JS the Math.Random returns the same type (Number). So it is only up to implementation if the Random will use its full range (cca 2^53). Or you can use the uuid which is 128bit long. Collision with even 2^32 is so unprobable that it is not important. However the biggest problem is that is is not about the running out of number but about the fact, that you can generate two same ids by accident - i.e. using the id from last page load in combination with the new id. – Radek Hladík Mar 12 '22 at 13:22
  • 1
    @Shadow but the second problem is that you are creating a code that can not be paralelized. I know that JS is a single thread environment but it is considered a bad habit to create unnecessary limitations in your code - i.e. when it is not needed by the problem that you are trying to solve. Now it may seem fine but later the "developer" may think that the top button on the page has ID of 5 (because it has always had) and then you run it in different browser or add another id somewhere and it will change. Basically do not impose unnecessary limitations on your code if you do not have to... – Radek Hladík Mar 12 '22 at 13:28
  • @radekhladik for starters, math.random returns a float between 0 and 1. So no, the full range of Number is not available even if they housed in the same type. Second, as you noted, JavaScript is a single thread environment. Sure, this will not work as expected in a multi threaded environment, but to account for that in this one is overengineering. – Shadow Mar 13 '22 at 20:54
  • @shadow You should probably learn, how float numbers work... There is a thing called exponent that makes it possible to use the whole range even with numbers between 0-1. Or put simply: it can either have bigger accuracy on small range or hold larger range with less accuracy... – Radek Hladík Mar 14 '22 at 21:08
  • @RadekHladík I think at this point, it's best to agree that we disagree. We've made our points in the comments - readers can decide for themselves. – Shadow Mar 14 '22 at 21:58
-10

It can also be achieved using this pattern (Vue 2.0 v-bind) , so let say you have a list of items to iterate over and you want to give some dom element uninque id's.

new Vue({

  el:body,
  data: {
     myElementIds : [1,2,3,4,5,6,8]
   }
})

Html

<div v-for="id in myElementIds">
    <label v-bind:for="id">Label text for {{id}}</label>
    <input v-bind:id="id" type="text" />
<div> 

Hope it helps

pitaside
  • 650
  • 10
  • 14