How do I create unique element ids with Vue?
The id
attribute for an element needs to be unique across the entire page. Hardcoding an element id
within a component could lead to clashes, and will make it difficult to reuse that component. It's also rarely necessary, as styling should use classes instead.
But there are legitimate use cases for an id
, such as ARIA attributes or tying together a <label>
and <input>
. In those scenarios you would need to generate a dynamic value for the id
.
Using a counter
In many cases, it's sufficient to use a simple counter to generate a suitable id
.
Put something like this in a .js
file:
let count = 0
export function newId(prefix = 'id-') {
return `${prefix}${++count}`
}
You can then use it in your components like this:
<script setup>
// Update the path to match the file you created
import { newId } from '@/utils/id.js'
const id = newId()
</script>
See it on the SFC Playground.
An element id
beginning with a digit can cause problems, so we put a prefix before the count.
Using useId()
The simple counter outlined above is typically sufficient for client-side applications, but it doesn't work well with SSR (server-side rendering).
With SSR, the id
attributes in the server-generated HTML need to match the equivalent client-side attributes. There are several reasons this is difficult, but the two biggest problems are:
- If the counter is global on the server, it will be shared between requests. It needs to be scoped to the application instance instead, to ensure it starts back at 0 when a new request comes in.
- The calls to the counter must happen in exactly the same order on both the server and client. While this is usually the case, it breaks down for async components.
Vue 3.5 introduced the useId()
helper, which aims to solve this problem. Prior to that, Nuxt and Quasar both implemented their own useId()
helpers. The approaches they took were very different, so it can be interesting to compare those alternatives:
- The Quasar
useId()
helper returns a ref. That ref initially has a value ofnull
. The value is only changed to a meaningful ID on the client-side after hydration has completed. Vue won't render attributes withnull
values, so any attribute bound to the ref won't be rendered until after hydration. - The Nuxt
useId()
helper is now just an alias for Vue's ownuseId()
, but prior to Vue 3.5 it took a very different approach. Nuxt would generate an ID on the server and then pass that to the client using a specialdata-n-ids
attribute in the generated HTML. This could then be checked immediately prior to hydration, allowing the client to use the same ID. A major downside of this approach was that it relied on having a suitable element node to hold thedata-n-ids
attribute.
If you're using Vue 3.5+ then you'll likely want to use the built-in useId()
helper to generate id
attributes for SSR. It uses multiple counters, taking account of the component tree and the positions of async components. These counters are concatenated to generate the final value.
<script setup>
import { useId } from 'vue'
const id = useId()
</script>
See it on the SFC Playground.
Other types of ID
The useId()
helper is only intended to solve the problem of generating attribute values, as described above. It is not a general-purpose way of generating IDs for other uses. For example, if you need to generate unique IDs for use in your data, then useId()
likely isn't what you want. In many cases you'll be able to use the counter approach described earlier. If you want a UUID instead then consider using the native crypto.randomUUID()
method or the uuid
package available on npm.