
If you have ever wired up a <label for="…"> to an <input id="…">, duplicated a component twice, and suddenly had duplicate IDs in the document, you already know why “just pick an id string” does not scale. Vue 3.5 added useId(), a small Composition API helper that generates unique-per-application identifiers that stay consistent between server and client renders.
Despite the casual phrase “random ids,” useId() is not a source of cryptographic randomness. It produces deterministic, stable strings that are unique within your Vue app instance. That distinction matters: you get uniqueness and SSR safety without Math.random() or global counters that fight hydration.
useId() existsManual patterns break down quickly:
Math.random() in setup gives different values on server and client, which can cause hydration mismatches in SSR apps.useId() centralizes ID generation in the framework so labels, aria-* attributes, form controls, and more stay valid and predictable.
useId()Common situations where you need a DOM-safe unique string (not cryptographic randomness):
aria-labelledby, aria-describedby, etc.)Import useId from vue and call it once per logical id you need in the component. Wire the returned string to id, for, or ARIA attributes as usual.
<script setup lang="ts">
import { useId } from "vue";
const nameFieldId = useId();
</script>
<template>
<form>
<label :for="nameFieldId">Name</label>
<input :id="nameFieldId" type="text" name="name" autocomplete="name" />
</form>
</template>Each call to useId() in the same component instance receives a different id. Each instance of the component receives ids distinct from other instances. That matches what you want for accessible, reusable field groups.
Need a pair for email and password? Call useId() separately for each control (or group).
<script setup lang="ts">
import { useId } from "vue";
const emailId = useId();
const passwordId = useId();
</script>
<template>
<div>
<label :for="emailId">Email</label>
<input :id="emailId" type="email" autocomplete="email" />
</div>
<div>
<label :for="passwordId">Password</label>
<input :id="passwordId" type="password" autocomplete="current-password" />
</div>
</template>According to the official API docs, ids from useId() are stable across server and client renders. You can use them in Nuxt, custom SSR setups, or any code path that renders on the server first without worrying that the client will “reroll” different strings during hydration.
If more than one Vue application mounts on the same document, you can reduce the chance of clashes between apps by setting an id prefix on each app:
import { createApp } from "vue";
import AdminRoot from "./AdminRoot.vue";
const app = createApp(AdminRoot);
app.config.idPrefix = "admin";
app.mount("#admin-app");Use a different prefix per app instance so generated ids remain unique in the combined DOM.
useId() inside computed()You should avoid invoking useId() inside a computed() getter. As with other composables, calling it there can cause instance conflicts because id registration is tied to component setup order. Instead, create the id at the top level of <script setup> (or setup()) and close over it inside computeds or methods.
<script setup lang="ts">
import { computed, useId } from "vue";
const fieldId = useId();
// Good: fieldId is fixed for this instance; computed only derives display logic.
const describedBy = computed(() => `${fieldId}-hint`);
</script>useId() is extremely useful on the frontend, but it is not the answer for every problem. Here are some cases where you might be tempted to reach for useId() but should not:
v-for when list identity should follow data (prefer a real id from your database model)crypto.randomUUID() or server-issued IDs instead of useId()For everyday UI plumbing, though, useId() removes a whole class of duplicate-id and SSR bugs with almost no API surface.
To sum up, useId() is a powerful tool for generating unique ids for your application. It's a simple, composable API that helps you avoid duplicate-id and SSR bugs with almost no API surface.
app.config.idPrefix to prevent cross-app id collisions when mounting multiple appscomputed(); generate the id in setup scope


Our goal is to be the number one source of Vue.js knowledge for all skill levels. We offer the knowledge of our industry leaders through awesome video courses for a ridiculously low price.
More than 200.000 users have already joined us. You are welcome too!
© All rights reserved. Made with ❤️ by BitterBrains, Inc.