Home / Blog / Generating Random IDs in Vue.js
Generating Random IDs in Vue.js

Generating Random IDs in Vue.js

Daniel Kelly
Daniel Kelly
Updated: April 3rd 2026

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.

Why useId() exists

Manual patterns break down quickly:

  • Hard-coded IDs collide when the same component is used more than once.
  • Math.random() in setup gives different values on server and client, which can cause hydration mismatches in SSR apps.
  • Hand-rolled incrementing counters are easy to get wrong across async boundaries or shared modules.

useId() centralizes ID generation in the framework so labels, aria-* attributes, form controls, and more stay valid and predictable.

Use Cases for useId()

Common situations where you need a DOM-safe unique string (not cryptographic randomness):

  • Creating unique DOM element IDs for anchor links
  • Associating a label and input in a reusable form field component
  • Anchoring headings for in-page navigation (table of contents)
  • Assigning a unique id to custom tooltip or popover elements
  • Distinguishing multiple error callouts or alerts in a single view
  • Generating ids for ARIA attributes (aria-labelledby, aria-describedby, etc.)
  • Marking tab panels and tab buttons with unique relationships
  • Disambiguating ids in nested reusable components (e.g., accordions, tabs)
  • Generating ids for form controls created at runtime (e.g., survey builders)

Basic usage

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.

Multiple ids in one component

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>

SSR and hydration

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.

Multiple Vue apps on one page

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.

Important: do not call 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>

When you might still use something else

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:

  • Entity primary keys from your API—these should be generated by your database or API server
  • Stable keys in v-for when list identity should follow data (prefer a real id from your database model)
  • Correlation IDs for API requests—they should be unpredictable or traceable by policy; use crypto.randomUUID() or server-issued IDs instead of useId()
  • Security-sensitive tokens—use proper random or server-issued secrets

For everyday UI plumbing, though, useId() removes a whole class of duplicate-id and SSR bugs with almost no API surface.

Summary

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.

  • Uniqueness - Unique per Vue application; distinct for each component instance and on every call
  • SSR - Produces the same id on server and client—safe for hydration
  • Multiple apps - Use app.config.idPrefix to prevent cross-app id collisions when mounting multiple apps
  • PitfallDo - do not call inside computed(); generate the id in setup scope

Start learning Vue.js for free

Daniel Kelly
Daniel Kelly
Daniel is the lead instructor at Vue School and enjoys helping other developers reach their full potential. He has 10+ years of developer experience using technologies including Vue.js, Nuxt.js, and Laravel.

Comments

Latest Vue School Articles

Using Pretext in Vue to Build Variable-Height UI Without Layout Thrash

Using Pretext in Vue to Build Variable-Height UI Without Layout Thrash

Learn how to use Pretext in Vue to measure multiline text without hidden DOM probes, forced reflow, or brittle getBoundingClientRect loops.
Daniel Kelly
Daniel Kelly
RAG with Nuxt and Gemini File Search

RAG with Nuxt and Gemini File Search

Build a practical, end-to-end RAG app with Nuxt and Gemini File Search. Index documents, retrieve grounded context, and answer user questions
Daniel Kelly
Daniel Kelly
VueSchool logo

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!

Follow us on Social

© All rights reserved. Made with ❤️ by BitterBrains, Inc.