useIdとは、コンポーネントごとに重複しないid文字列を作ってくれるReactのフックです。 label の htmlFor と input の id を結び付けるときや、aria-describedby で説明文を指すときに使います。同じ部品を画面に何個置いても id がぶつからず、サーバーで作った HTML とブラウザ側の描画(ハイドレーション)でも同じ値になります。

この記事では、同じ入力部品を2回置いたときに id が重複する HTML と、useId で直した HTML を renderToString の出力で比べます。 さらに、Math.random や連番カウンタで id を作るとハイドレーションで警告が出る様子を再現し、useId なら一致することを確かめました。生成される id の形は React のバージョンで変わるため、18.3.1・19.0.0・19.1.0・19.2.0・19.3.0 の5つで実際の値を並べています。

確認環境:Ubuntu 24.04 / Node.js 22.22.2 / React・react-dom 19.3.0(形式の比較に 18.3.1・19.0.0・19.1.0・19.2.0)/ esbuild 0.28.2 でビルド / Playwright 1.56.0 の Chromium 141 で実行。

useIdの早見表

やりたいこと 書き方 補足
label と input を結び付ける const id = useId() → htmlFor={id} と id={id} 部品を何個置いても id が重ならない
1つの部品に入力が複数ある id + '-first'、id + '-last' のように接尾辞を付ける useId の呼び出しは1回でよい
説明文やエラー文を入力欄に結び付ける aria-describedby={id + '-hint'} と、説明文側の id={id + '-hint'} 入力欄の補足説明として関連付けられる
1ページに React アプリを複数載せる createRoot / hydrateRoot / サーバー側 API に identifierPrefix を渡す サーバーとクライアントで同じ値にする
リストの key を作る useId は使わず、データが持つ ID を使う 同じ値になり警告が出る(後述)

useId が向く場面と、別の方法を選ぶ場面です。

用途 useId 代わりに使うもの
htmlFor と id の組 向いている ―
aria-describedby などの参照先 向いている ―
リストの key 向かない データの ID(task.taskId など)
CSS のセレクタ、テスト用の目印 向かない className、data-testid などの固定の名前
データベースに保存する ID 向かない サーバー側で採番した値

実際に生成された id の形式(詳しくは後述)です。

React クライアントだけで描画 サーバーで描画
18.3.1 / 19.0.0 :r0: :R1:
19.1.0 «r0» «R1»
19.2.0 / 19.3.0 _r_0_ _R_1_

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「React useIdの使い方 labelとinputのid重複を防ぐ」

準備:renderToStringとハイドレーションを試せる最小構成

サーバーで HTML を作る処理と、ブラウザでハイドレーションする処理を、esbuild でそれぞれ1ファイルにまとめて動かします。

mkdir useid-demo && cd useid-demo
npm init -y
npm i react react-dom
npm i -D esbuild typescript @types/react @types/react-dom
useid-demo/
├─ make-page.mjs      … HTML とスクリプトを1ページにまとめる
└─ src/
   ├─ UseIdField.tsx  … 入力部品(以降、版ごとに差し替える)
   ├─ App.tsx         … 部品を2回置く
   ├─ ssr.tsx         … renderToString で HTML を出力
   └─ client.tsx      … hydrateRoot でハイドレーション
// src/App.tsx
import { UseIdField } from './UseIdField'

export function App() {
  return (
    <form>
      <UseIdField label="名前" />
      <UseIdField label="担当者名" />
    </form>
  )
}
// src/ssr.tsx
import { renderToString } from 'react-dom/server'
import { App } from './App'

// 第2引数の identifierPrefix は省略できる
const prefix = process.argv[2]
const html = renderToString(<App />, prefix ? { identifierPrefix: prefix } : undefined)
console.log(html)
// src/client.tsx
import { hydrateRoot } from 'react-dom/client'
import { App } from './App'

// サーバー側と同じ identifierPrefix を渡す
const prefix = document.body.dataset.prefix
hydrateRoot(document.getElementById('root')!, <App />, prefix ? { identifierPrefix: prefix } : undefined)
// make-page.mjs
import { execFileSync } from 'node:child_process'
import { readFileSync, writeFileSync } from 'node:fs'

// サーバーで作った HTML を root に入れ、クライアント用のスクリプトを読み込むページを作る
const html = execFileSync('node', ['out/ssr.cjs']).toString().trim()
const js = readFileSync('out/client.js', 'utf8')
writeFileSync(
  'out/index.html',
  `<!doctype html><meta charset="utf-8"><body><div id="root">${html}</div><script>${js}</script></body>`,
)

ビルドと実行のコマンドです。警告を見たいので、クライアント側は開発ビルドにしています。

npx esbuild src/ssr.tsx --bundle --platform=node --jsx=automatic --outfile=out/ssr.cjs
npx esbuild src/client.tsx --bundle --jsx=automatic '--define:process.env.NODE_ENV="development"' --outfile=out/client.js
node out/ssr.cjs        # HTML を表示
node make-page.mjs      # out/index.html を作る(ブラウザで開く)

以降に載せる HTML は、読みやすいようにタグの間で改行しています。

idを固定で書いた部品を2回置くと重複する

まず、id を固定の文字列で書いた入力部品です。1個だけ置くぶんには問題なく動きます。

// src/FixedIdField.tsx
// id を固定の文字列で書いた入力部品
export function FixedIdField({ label }: { label: string }) {
  return (
    <div>
      <label htmlFor="name">{label}</label>
      <input id="name" type="text" />
    </div>
  )
}

これを「名前」「担当者名」の2か所で使い、renderToString で HTML にした結果です。

<form>
<div>
<label for="name">名前</label>
<input id="name" type="text"/>
</div>
<div>
<label for="name">担当者名</label>
<input id="name" type="text"/>
</div>
</form>

id="name" が2つ、for="name" も2つあります。 HTML では id はページ内で1つだけという決まりなので、ブラウザは最初に見つかった要素を採用します。このページを Chromium で開き、2つ目のラベル(担当者名)をクリックした結果です。

ハイドレーション後の input の id: ["name","name"]
2つ目のラベルをクリック → フォーカスが当たった input: 1 番目
同じ id を持つ要素の数: {"root":1,"name":2}

「担当者名」をクリックしたのに、フォーカスは1番目(名前)の入力欄に移りました。 エラーも警告も出ないまま、ラベルと入力欄の対応がずれています。

props で id を渡してもらう方法でも直せますが、使う側が毎回ぶつからない名前を考える必要があります。そこで useId を使います。

useIdで直す:部品ごとに別のidが付く

// src/UseIdField.tsx
import { useId } from 'react'

// useId で id を作る入力部品
export function UseIdField({ label }: { label: string }) {
  const id = useId()
  return (
    <div>
      <label htmlFor={id}>{label}</label>
      <input id={id} type="text" />
    </div>
  )
}

変更点は、const id = useId() を足して、固定の文字列を {id} に置き換えただけです。引数はありません。renderToString の出力です。

<form>
<div>
<label for="_R_1_">名前</label>
<input id="_R_1_" type="text"/>
</div>
<div>
<label for="_R_2_">担当者名</label>
<input id="_R_2_" type="text"/>
</div>
</form>

同じ操作をした結果です。

ハイドレーション後の input の id: ["_R_1_","_R_2_"]
2つ目のラベルをクリック → フォーカスが当たった input: 2 番目
同じ id を持つ要素の数: {"root":1,"_R_1_":1,"_R_2_":1}

1つ目は _R_1_、2つ目は _R_2_ と別の id になり、2つ目のラベルで2番目の入力欄にフォーカスが移りました。 部品を使う側は id を意識する必要がありません。

直す前と後の比較

項目 固定の id useId
部品を2回置いたときの id name と name(重複) _R_1_ と _R_2_
2つ目のラベルをクリック 1番目の入力欄にフォーカス 2番目の入力欄にフォーカス
使う側が id を考える必要 あり なし
コンソールの警告 なし(気づきにくい) なし

生成されるidの形式はバージョンで違う

useId が返す文字列の形は、React のバージョンによって変わります。同じコードを5つのバージョンで動かし、クライアントだけで描画した場合(createRoot)とサーバーで描画した場合(renderToString)の値を調べました。

React createRoot での1個目・2個目 renderToString での1個目・2個目 同じ部品で2回呼んだとき(サーバー) identifierPrefix に app1-
18.3.1 :r0: :r1: :R1: :R2: :R0: :R0H1: :app1-R1:
19.0.0 :r0: :r1: :R1: :R2: :R0: :R0H1: :app1-R1:
19.1.0 «r0» «r1» «R1» «R2» «R0» «R0H1» «app1-R1»
19.2.0 _r_0_ _r_1_ _R_1_ _R_2_ _R_0_ _R_0H1_ _app1-R_1_
19.3.0 _r_0_ _r_1_ _R_1_ _R_2_ _R_0_ _R_0H1_ _app1-R_1_

読み取れることを整理します。

  • 18.3.1 と 19.0.0 はコロンで囲んだ形、19.1.0 は二重山かっこ(« »)、19.2.0 以降は下線を使った形でした
  • クライアントだけで描画すると小文字の r と連番、サーバーで描画すると大文字の R になります。サーバー側の値は、コンポーネントが木構造のどこにあるかから決まります
  • 1つのコンポーネントで useId を2回呼ぶと、2個目には H1 が付いた別の値が返ります
  • identifierPrefix は、囲みの記号のすぐ内側に入ります

このように形式は変わることがあるので、「id の中身がこの形である」ことに頼った処理は書かないのが安全です。文字列を分解したり、特定の値と比較したりせず、受け取った値をそのまま属性に渡す使い方にとどめます。

2回呼んだときのコードと出力

// src/TwoIds.tsx
import { useId } from 'react'

// 1つのコンポーネントで useId を2回呼ぶ
export function TwoIds() {
  const a = useId()
  const b = useId()
  return <p data-a={a} data-b={b}>two ids</p>
}
<p data-a="_R_0_" data-b="_R_0H1_">two ids</p>

2回呼んでもかまいませんが、次の章の接尾辞を使えば1回で足ります。

1つのidに接尾辞を付けて複数の入力とaria-describedbyに使う

1つの部品に入力欄がいくつもあるとき、入力欄の数だけ useId を呼ぶ必要はありません。useId は1回だけ呼び、接尾辞を足して使い分けます。 説明文を入力欄に結び付ける aria-describedby にも同じ方法が使えます。

// src/ProfileFields.tsx
import { useId } from 'react'

// 1つの id に接尾辞を付けて、複数の入力と説明文に使う
export function ProfileFields() {
  const id = useId()
  return (
    <fieldset>
      <legend>会員情報</legend>
      <label htmlFor={id + '-last'}>姓</label>
      <input id={id + '-last'} type="text" />
      <label htmlFor={id + '-first'}>名</label>
      <input id={id + '-first'} type="text" />
      <label htmlFor={id + '-pw'}>パスワード</label>
      <input id={id + '-pw'} type="password" aria-describedby={id + '-pw-hint'} />
      <p id={id + '-pw-hint'}>8文字以上で入力してください</p>
    </fieldset>
  )
}

renderToString の出力です。

<fieldset>
<legend>会員情報</legend>
<label for="_R_0_-last">姓</label>
<input id="_R_0_-last" type="text"/>
<label for="_R_0_-first">名</label>
<input id="_R_0_-first" type="text"/>
<label for="_R_0_-pw">パスワード</label>
<input id="_R_0_-pw" type="password" aria-describedby="_R_0_-pw-hint"/>
<p id="_R_0_-pw-hint">8文字以上で入力してください</p>
</fieldset>

元の id(_R_0_)が部品ごとに違うので、接尾辞を付けた id も部品ごとに違う値になります。この部品を何個置いても重なりません。

属性 役割 この例での値
htmlFor(HTML では for) ラベルが指す入力欄の id _R_0_-pw
id 要素の名前 _R_0_-pw、_R_0_-pw-hint
aria-describedby 入力欄の補足説明がある要素の id _R_0_-pw-hint

Math.randomや連番でidを作るとハイドレーションで警告が出る

「重ならない id が欲しいだけなら、乱数や連番でも作れるのでは」と考えるかもしれません。ブラウザだけで描画するアプリなら動きますが、サーバーで HTML を作ってブラウザでハイドレーションする構成では、サーバーとブラウザで値が食い違います。 実際に確かめます。

Math.randomで作った場合

// src/RandomIdField.tsx
import { useState } from 'react'

// Math.random で id を作る入力部品
export function RandomIdField({ label }: { label: string }) {
  const [id] = useState(() => 'f-' + Math.random().toString(36).slice(2, 8))
  return (
    <div>
      <label htmlFor={id}>{label}</label>
      <input id={id} type="text" />
    </div>
  )
}

サーバーが出力した HTML です。

<form>
<div>
<label for="f-ngbs4e">名前</label>
<input id="f-ngbs4e" type="text"/>
</div>
<div>
<label for="f-ckbb43">担当者名</label>
<input id="f-ckbb43" type="text"/>
</div>
</form>

この HTML をブラウザでハイドレーションしたときのコンソール出力(抜粋)です。

[console.error] A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:

- A server/client branch `if (typeof window !== 'undefined')`.
- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.
- Date formatting in a user's locale which doesn't match the server.
- External changing data without sending a snapshot of it along with the HTML.
- Invalid HTML tag nesting.

  <random>
    <form>
      <RandomIdField label="名前">
        <div>
          <label
+           htmlFor="f-gblu5b"
-           htmlFor="f-ngbs4e"
          >
          <input
+           id="f-gblu5b"
-           id="f-ngbs4e"
            type="text"
          >

ハイドレーション後の input の id: ["f-ngbs4e","f-ckbb43"]

+ がブラウザ側で計算した値、- がサーバーの HTML にあった値です。乱数は呼ぶたびに変わるので、サーバーでは f-ngbs4e、ブラウザでは f-gblu5b と別の値になりました。メッセージの原因一覧にも Math.random が挙がっています。

メッセージに「This won't be patched up」とあるとおり、ハイドレーション後も DOM の id はサーバーの値(f-ngbs4e)のままでした。React が持っている値と画面の値がずれた状態になります。

連番カウンタで作った場合

// src/CounterIdField.tsx
import { useState } from 'react'

let counter = 0

// モジュール変数の連番で id を作る入力部品
export function CounterIdField({ label }: { label: string }) {
  const [id] = useState(() => 'f-' + ++counter)
  return (
    <div>
      <label htmlFor={id}>{label}</label>
      <input id={id} type="text" />
    </div>
  )
}

連番は、最初の1回だけ見ると一致します。サーバーの1回目の描画は f-1 と f-2、ブラウザも f-1 と f-2 になり、警告は出ませんでした。

== 1回目のリクエストの HTML をハイドレーション
ハイドレーション後の input の id: ["f-1","f-2"]

ところが、サーバーのプロセスは動き続けるので、カウンタは増え続けます。同じサーバーで2回目に描画した HTML は f-3 と f-4 になり、ブラウザ側(毎回 1 から数える)と食い違います。

<form>
<div>
<label for="f-3">名前</label>
<input id="f-3" type="text"/>
</div>
<div>
<label for="f-4">担当者名</label>
<input id="f-4" type="text"/>
</div>
</form>
== 2回目のリクエストの HTML をハイドレーション
[console.error] A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. …
      <CounterIdField label="名前">
        <div>
          <label
+           htmlFor="f-1"
-           htmlFor="f-3"
          >
          <input
+           id="f-1"
-           id="f-3"
            type="text"
          >
      <CounterIdField label="担当者名">
        <div>
          <label
+           htmlFor="f-2"
-           htmlFor="f-4"
          >
          <input
+           id="f-2"
-           id="f-4"
            type="text"
          >

ハイドレーション後の input の id: ["f-3","f-4"]

手元で1回だけ試したときには気づかず、アクセスが重なってから現れるのが連番の特徴です。公式ドキュメントも、連番カウンタではサーバーが HTML を出力した順序とブラウザがハイドレーションする順序が一致するとは限らず、保証が難しいと説明しています。

useIdならサーバーとブラウザで一致する

同じ手順を useId の部品(UseIdField)で行った結果です。

== useId の HTML をハイドレーション
ハイドレーション後の input の id: ["_R_1_","_R_2_"]

コンソールには何も出ませんでした。サーバーで何回描画しても _R_1_ と _R_2_ のままです。useId の値は、乱数や呼び出し回数ではなくコンポーネントが木構造のどの位置にあるかから作られるので、同じ構造を描画すれば同じ値になります。

id の作り方 サーバー1回目 サーバー2回目 ブラウザ ハイドレーションの警告
Math.random() f-ngbs4e f-t0qtkl f-gblu5b 毎回出る
連番カウンタ f-1 f-3 f-1 2回目以降に出る
useId() _R_1_ _R_1_ _R_1_ 出ない

identifierPrefix:1ページに複数のReactアプリを載せるとき

1つのページに、別々にビルドした React アプリを2つ載せる場合を考えます。それぞれのアプリは自分の中で 0 から数えるので、id が重なります。同じ部品を使う2つのスクリプトを1ページで動かした結果です。

input の id: ["_r_0_","_r_1_","_r_0_","_r_1_"]

_r_0_ と _r_1_ が2組でき、重複しました。 これを防ぐのが identifierPrefix です。createRoot の第2引数に渡すと、そのルートの中で useId が作る id に接頭辞が付きます。

// src/client.tsx(createRoot で2つのルートを作る例)
import { createRoot } from 'react-dom/client'
import { App } from './App'

createRoot(document.getElementById('root1')!, { identifierPrefix: 'app1-' }).render(<App />)
createRoot(document.getElementById('root2')!, { identifierPrefix: 'app2-' }).render(<App />)
input の id: ["_app1-r_0_","_app1-r_1_","_app2-r_2_","_app2-r_3_"]

サーバーで描画する場合は、サーバー側の API とブラウザ側の hydrateRoot の両方に同じ値を渡します。準備の章の ssr.tsx と client.tsx はこの形になっています。3通りの組み合わせで確かめました。

サーバー側 ブラウザ側(hydrateRoot) サーバーの id 結果
指定なし 指定なし _R_1_ _R_2_ 警告なし
app1- app1- _app1-R_1_ _app1-R_2_ 警告なし
app1- 指定なし _app1-R_1_ _app1-R_2_ ハイドレーション不一致の警告

片方だけに指定したときのコンソール出力(抜粋)です。

[console.error] A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. …
+           htmlFor="_R_1_"
-           htmlFor="_app1-R_1_"
+           id="_R_1_"
-           id="_app1-R_1_"
指定する場所 書き方
ブラウザだけで描画 createRoot(container, { identifierPrefix: 'app1-' })
サーバーで HTML を作る renderToString(element, { identifierPrefix: 'app1-' })
ハイドレーション hydrateRoot(container, element, { identifierPrefix: 'app1-' })

keyやCSSセレクタに使わない理由

リストのkeyに使うと同じ値になる

useId はフックなので、ループの中では呼べません。そこでコンポーネントの先頭で1回呼んだ値を key に使うと、全部の行が同じ key になります。

// src/KeyList.tsx
import { useId } from 'react'

const tasks = [
  { taskId: 't-101', title: '見積もりを送る' },
  { taskId: 't-102', title: '会議室を予約する' },
]

// useId の値を key に使った例
export function KeyListWithUseId() {
  const id = useId()
  return (
    <ul>
      {tasks.map((task) => (
        <li key={id}>{task.title}</li>
      ))}
    </ul>
  )
}

// データが持つ ID を key に使う
export function KeyListWithData() {
  return (
    <ul>
      {tasks.map((task) => (
        <li key={task.taskId}>{task.title}</li>
      ))}
    </ul>
  )
}
== KeyListWithUseId
[console.error] Encountered two children with the same key, `_r_0_`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
== KeyListWithData
(コンソール出力なし)

key は「どのデータの行か」を React に伝えるためのものです。公式ドキュメントも、key はデータから作るものとして、useId を key の生成に使わないよう案内しています。key にはデータが持つ ID(この例では taskId)を使います。

CSSセレクタに使うと、バージョンと描画方法で結果が変わる

useId の値を querySelector のセレクタに使えるかを、バージョンごとに試しました。

== React 18.3.1 / 19.0.0(id は :r0:)
querySelector('#:r0:') → SyntaxError: Failed to execute 'querySelector' on 'Document': '#:r0:' is not a valid selector.
querySelector('#' + CSS.escape(id)) → INPUT / CSS.escape(id) = \:r0\:
getElementById(id) → INPUT

== React 19.1.0(id は «r0»)
querySelector('#«r0»') → INPUT

== React 19.2.0 / 19.3.0(id は _r_0_)
querySelector('#_r_0_') → INPUT
getElementById(id) → INPUT

コロンを含む形式では、そのままではセレクタとして解釈できず SyntaxError になります。19.1.0 以降の形式ではセレクタとしても通りました。ただし、通るからといって CSS ファイルに #_r_0_ と書くのは向きません。理由は3つあります。

理由 この記事で確かめた内容
値がバージョンで変わる :r0: → «r0» → _r_0_
描画方法で変わる クライアントだけなら _r_0_、サーバーで描画すると _R_1_
部品の位置や数で変わる 1個目は _R_1_、2個目は _R_2_。部品を足すと番号がずれる

スタイルやテストの目印には、className や data-testid のような固定の名前を使います。JavaScript から id で要素を探すなら、document.getElementById(id) が今回試したどのバージョンでも動きました。

そのほかの注意点(公式ドキュメントより)

注意点 内容
呼べる場所 コンポーネントか自作フックのトップレベルだけ。ループや条件分岐の中では呼べない
use() のキャッシュのキー 生成に使わない。キーはデータから作る
async のサーバーコンポーネント 現時点では useId を使えない

つまずきやすい点と直し方

症状 原因 直し方
ラベルをクリックすると別の入力欄にフォーカスが移る 固定の id が重複している 部品の中で useId を使う
ハイドレーションで id の不一致の警告が出る 乱数・連番・時刻などで id を作っている useId に置き換える
useId なのに不一致の警告が出る identifierPrefix がサーバーとブラウザで違う 両方に同じ値を渡す
複数の React アプリで id が重なる 別々のアプリがそれぞれ 0 から数えている アプリごとに identifierPrefix を変える
「Encountered two children with the same key」と出る useId の値を key に使っている データの ID を key にする
querySelector で SyntaxError になる(18.3.1・19.0.0) id にコロンが含まれる getElementById か CSS.escape を使う。目印は固定の名前にする

よくある質問

Q. 入力欄ごとに useId を呼ぶ必要がありますか? A. 1つのコンポーネントで1回呼び、id + '-first' のように接尾辞を付ければ足ります。2回呼んでも動き、19.3.0 では _R_0_ と _R_0H1_ のように別の値が返りました。

Q. サーバーで描画しないアプリでも useId を使う意味はありますか? A. あります。部品を何個置いても id が重ならないという利点は、ブラウザだけで描画するアプリでも同じです。乱数や連番を自分で管理する必要がなくなり、あとからサーバーでの描画を取り入れたときもそのまま動きます。

Q. label で input を囲めば、id は要りませんか? A. label の中に input を入れれば、htmlFor と id がなくてもラベルと入力欄は結び付きます(Chromium 141 で、ラベルの文字をクリックすると中の入力欄にフォーカスが移ることを確かめました)。ラベルと入力欄を別の場所に置きたいときや、aria-describedby で説明文を指すときは id が必要になるので、そこで useId が役立ちます。

Q. useId の値を固定の文字列だと思ってテストに書いてよいですか? A. 向きません。形式はバージョンで変わり(18.3.1 は :r0:、19.3.0 は _r_0_)、部品の位置でも番号が変わります。テストではラベルの文言や role、data-testid で要素を探す書き方が安定します。

まとめ

  • useId は引数なしで呼び、返った文字列を htmlFor と id に渡す。 同じ部品を何個置いても id が重ならない
  • 固定の id を2回置くと、2つ目のラベルが1番目の入力欄を指す。 エラーが出ないので気づきにくい
  • Math.random や連番で作った id は、ハイドレーションで不一致の警告が出る。 useId はサーバーとブラウザで同じ値になる
  • id の形式はバージョンで変わる。 18.3.1・19.0.0 は :r0:、19.1.0 は «r0»、19.2.0 以降は _r_0_。中身の形に頼らない
  • 入力が複数あるときは接尾辞を付ける。 aria-describedby の参照先にも同じ方法が使える
  • 複数の React アプリを1ページに載せるときは identifierPrefix。 サーバーとブラウザで同じ値を渡す
  • key とCSSセレクタには使わない。 key はデータの ID、目印は固定の名前にする

参考・出典

  • React 公式ドキュメント「useId」 https://react.dev/reference/react/useId

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「React useIdの使い方 labelとinputのid重複を防ぐ」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?