Blog
Wild & Free Tools

UUID as React and Vue List Keys: When and How

Last updated: January 2026 5 min read
Quick Answer

Table of Contents

  1. The key Prop
  2. Server IDs
  3. Client-Only Lists
  4. Array Index vs UUID
  5. useId Hook
  6. Frequently Asked Questions

React's key prop and Vue's :key binding exist for one reason: to tell the framework which DOM node corresponds to which data item across renders. Use a stable unique ID and the framework reuses the right DOM node. Use an unstable value (like array index or a freshly generated UUID) and you get unnecessary re-mounts, lost input state, and broken animations. UUIDs are excellent keys — but only when assigned at the right time.

Why key Matters: How React and Vue Track List Items

When React (or Vue) renders a list and the list changes, it needs to figure out what changed. The key prop is the identifier it uses:

If every render generates a new UUID for the same item, React sees a new key every time — it unmounts and remounts the component instead of updating it. This loses input state, triggers mount animations again, and is slower than a simple update.

// ❌ WRONG — generates new UUID every render
{items.map(item => (
  <Item key={crypto.randomUUID()} data={item} />
))}

// ✓ CORRECT — use the stable ID from your data
{items.map(item => (
  <Item key={item.id} data={item} />
))}

Best Case: Use the UUID from Your Database

If your items come from a database with UUID primary keys, the answer is simple — use the existing ID:

// React — fetched orders from API, each has a UUID id
function OrderList({ orders }) {
  return (
    <ul>
      {orders.map(order => (
        <li key={order.id}>
          {order.customerEmail} — ${order.total}
        </li>
      ))}
    </ul>
  );
}
<!-- Vue 3 -->
<ul>
  <li v-for="order in orders" :key="order.id">
    {{ order.customerEmail }} — {{ order.total }}
  </li>
</ul>

The UUID from your API response is already globally unique and stable across all renders. Nothing else needed.

Sell Custom Apparel — We Handle Printing & Free Shipping

Client-Only Lists: Assign UUID Once at Creation Time

For local state that never hits a server — like a dynamic form where users add rows — assign the UUID when the item is created, not during render:

import { useState } from 'react';

function DynamicForm() {
  const [rows, setRows] = useState([
    { id: crypto.randomUUID(), label: '', value: '' }
  ]);

  const addRow = () => {
    setRows(prev => [
      ...prev,
      { id: crypto.randomUUID(), label: '', value: '' }  // UUID assigned HERE
    ]);
  };

  const removeRow = (id) => {
    setRows(prev => prev.filter(row => row.id !== id));
  };

  return (
    <div>
      {rows.map(row => (
        <FormRow key={row.id} row={row} onRemove={removeRow} />
        // key={row.id} is stable — same UUID across all re-renders
      ))}
      <button onClick={addRow}>Add Row</button>
    </div>
  );
}

The UUID is generated once in addRow() and stored in state. Every time the component re-renders, row.id is the same value — so React reuses the DOM node instead of remounting it.

Array Index vs UUID: Which Is Worse?

Many tutorials say "use array index as key if you don't have an ID." This is worse than UUID in reordering scenarios:

UUID keys are strictly better than index keys whenever items can be reordered, filtered, or deleted. Array index is only safe for static lists that never change order.

// ❌ Index key — breaks on reorder
{items.map((item, index) => <Item key={index} {...item} />)}

// ✓ UUID key — stable on reorder
{items.map(item => <Item key={item.id} {...item} />)}

React 18+: useId Hook for Component-Scoped IDs

React 18 introduced useId() — a hook that generates a stable, unique ID scoped to a component instance. It's designed for accessibility attributes (htmlFor, aria-labelledby), not list keys:

import { useId } from 'react';

function FormField({ label }) {
  const id = useId();  // stable across renders, unique per instance
  return (
    <div>
      <label htmlFor={id}>{label}</label>
      <input id={id} type="text" />
    </div>
  );
}

useId() is not for list keys — it generates IDs based on component tree position, which changes when items are reordered. For list keys, stick with UUID from your data or from state.

Use the free UUID generator above to create test IDs for React and Vue unit tests, Storybook stories, or mock API responses.

Generate UUID v4 Values for React and Vue Tests

The Cheetah UUID Generator produces RFC-compliant UUID v4 strings — paste them as mock IDs in Storybook stories, Jest test fixtures, or Vitest mocks.

Open Free UUID Generator

Frequently Asked Questions

Can I use crypto.randomUUID() directly as a key in JSX?

Only if you call it outside render — stored in state or computed from stable data. Calling crypto.randomUUID() directly inside map() generates a new UUID on every render, causing constant remounts.

Does using UUID keys improve React performance?

Stable UUID keys improve performance compared to index keys for reordering scenarios, because React correctly identifies unchanged items and skips their updates. Random UUID keys (generated in render) destroy performance.

What's the React useId hook for if not for list keys?

useId() is for accessibility — linking labels to inputs via htmlFor/id, aria-labelledby, and similar attribute pairs. It generates a component-tree-stable ID that works in server-side rendering. It's explicitly not designed for list keys.

Should Vue :key and React key be the same field?

They serve the same purpose and the same rules apply — use a stable, unique identifier from your data. The implementation is different (Vue uses :key directive, React uses key prop) but the best practices are identical.

Kevin Harris
Kevin Harris Finance & Calculator Writer

Kevin is a certified financial planner passionate about making financial literacy tools free and accessible.

More articles by Kevin →
Launch Your Own Clothing Brand — No Inventory, No Risk