Apa yang Dikeluhkan Error Ini

Ketika kamu mengembangkan aplikasi React, suatu saat kamu hampir pasti akan menjumpai pesan error berikut di console browser:

Warning: Each child in a list should have a unique "key" prop.

Check the render method of `ComponentName`. See https://reactjs.org/docs/lists-and-keys.html for more information.

Pesan ini muncul sebagai warning, bukan error fatal—aplikasi tetap berjalan, tapi React secara eksplisit memberi tahu bahwa ada sesuatu yang salah dalam cara kamu merender daftar elemen. Dalam bahasa Indonesia sederhana, pesan ini berarti: setiap elemen anak yang di-render melalui loop atau pemetaan array wajib memiliki properti key yang unik. Tanpa key yang unik, React tidak bisa membedakan satu elemen dari elemen lainnya dalam daftar, sehingga proses reconciling (pembaruan DOM virtual) menjadi tidak efisien dan berpotensi menghasilkan bug visual yang sulit dilacak.

Kenapa error ini muncul? Secara teknis, ketika kamu menggunakan Array.prototype.map() atau loop sejenis untuk merender kumpulan komponen, React membutuhkan identitas stabil untuk tiap item. Jika key tidak disertakan, React secara default akan menggunakan index posisi array sebagai key—dan ini yang berbahaya karena index bersifat dinamis. Saat item ditambah, dihapus, atau di-reorder, index berubah, dan React bisa salah menganggap sebuah elemen sebagai elemen lain. Inilah mengapa React memancarkan warning ini: bukan sekadar formalitas, melainkan indikator nyata bahwa rendering-mu berisiko mengalami inconsistency.

Perhatikan contoh kode berikut yang memicu warning ini:

function UserList({ users }) {
  return (
    <ul>
      {users.map((user) => (
        <li>{user.name}</li>
      ))}
    </ul>
  );
}

Pada potongan di atas, tidak ada prop key yang diberikan ke setiap <li>, sehingga React tidak punya cara untuk mengidentifikasi masing-masing item secara unik.

Akar Masalahnya

Ada beberapa root cause yang paling sering menjadi sumber warning ini. Urutkan dari yang paling frequently encountered:

Pertama, tidak memberikan prop key sama sekali saat memetakan array ke JSX. Ini adalah penyebab paling klasik dan paling sering ditemui, terutama di kalangan developer yang baru mengenal React. Developer menulis .map() dan langsung return JSX tanpa menyadari bahwa React mengharuskan atribut key pada elemen terluar yang di-return dari map.

Kedua, menggunakan index array sebagai key. Secara teknis ini tidak memicu warning—karena key memang ada—tapi ini adalah anti-pattern yang setara dampak buruknya dengan tidak memberi key sama sekali. Developer sering menulis key={index} karena merupakan cara paling mudah, namun saat list bersifat dinamis (item bisa dihapus, ditambah, atau di-sort), index yang berubah membuat React kehilangan track identitas item sebenarnya.

Ketiga, menggunakan nilai yang tidak unik sebagai key, misalnya field yang duplikat antar item. Contoh umum: menggunakan key={item.category} di mana beberapa item berbagi category yang sama. React akan memunculkan warning lain (Encountered two children with the same key), dan ini menyebabkan React memperlakukan item-item dengan key sama sebagai item yang sama, sehingga hanya merender satu di antaranya.

Keempat, fragment wrapper tanpa key. Ketika return dari map adalah <>...</> (React.Fragment shorthand), prop key tidak bisa diletakkan di sana. Developer lupa mengganti ke <React.Fragment key={...}>, sehingga warning tetap muncul meskipun mereka sudah "menambahkan" key di dalam fragment.

Kelima, nested mapping di mana key hanya diberikan di level outer loop tapi tidak di inner loop. Developer yang melakukan double .map() kadang hanya memberi key pada loop pertama dan lupa bahwa loop kedua juga memproduksi list yang membutuhkan key.

Untuk memastikan penyebab mana yang kamu alami, lakukan pemeriksaan berikut:

  • Cek apakah ada .map() di kode komponen yang bermasalah. Jika ada dan tidak ada key di elemen return-nya, itu penyebab pertama.
  • Jika key sudah ada tapi berupa index (key={i} atau key={index}), itu penyebab kedua.
  • Jika key menggunakan field data tapi warning berubah jadi "two children with the same key", cek apakah field tersebut benar-benar unik di seluruh item. Jika tidak, itu penyebab ketiga.
  • Jika return map menggunakan <>...</> dan key tidak bisa ditempel, itu penyebab keempat.
  • Jika ada .map() di dalam .map(), periksa kedua level—kemungkinan besar itu penyebab kelima.

Langkah Perbaikan

Langkah 1: Identifikasi lokasi yang memicu warning

Buka developer tools browser (F12), pergi ke tab Console, dan cari warning tersebut. React menyertakan nama komponen dan metode render yang bermasalah. Perhatikan output berikut:

Warning: Each child in a list should have a unique "key" prop.
Check the render method of `TodoList`.

Dari sini kamu tahu bahwa masalah ada di komponen TodoList. Buka file komponen tersebut.

Langkah 2: Cari pemanggilan .map() di komponen tersebut

Cari semua instance .map( di dalam JSX return komponen. Setiap .map() yang merender elemen React wajib diberi key.

Langkah 3: Tambahkan prop key menggunakan identifier unik dari data

Jika item memiliki id dari database, itu adalah kandidat terbaik untuk key:

function TodoList({ todos }) {
  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  );
}

Output yang diharapkan: warning di console menghilang sepenuhnya setelah hot-reload.

Langkah 4: Jika tidak ada id unik, gunakan kombinasi field yang unik

Kadang data belum punya field id. Kamu bisa mengkombinasikan dua atau lebih field yang secara bersama-sama membentuk identitas unik:

function ScheduleList({ schedules }) {
  return (
    <div>
      {schedules.map((s) => (
        <ScheduleCard key={`${s.day}-${s.timeSlot}`}>{s.title}</ScheduleCard>
      ))}
    </div>
  );
}

Langkah 5: Untuk Fragment shorthand, ganti ke bentuk eksplisit

Jika kamu menggunakan <>...</> sebagai wrapper di dalam map, ganti ke <React.Fragment>:

// ❌ Salah — key tidak bisa diletakkan di shorthand fragment
{items.map((item) => (
  <><span>{item.label}</span><span>{item.value}</span></>
))}

// ✅ Benar — gunakan Fragment eksplisit
{items.map((item) => (
  <React.Fragment key={item.id}>
    <span>{item.label}</span>
    <span>{item.value}</span>
  </React.Fragment>
))}

Langkah 6: Untuk nested mapping, beri key unik di setiap level

{categories.map((cat) => (
  <div key={cat.id}>
    {cat.items.map((item) => (
      <ItemCard key={item.id}>{item.name}</ItemCard>
    ))}
  </div>
))}

Verifikasi solusi berhasil

Setelah melakukan perbaikan, verifikasi dengan cara berikut:

  1. Refresh halaman dan buka Console—pastikan warning sudah hilang.
  2. Lakukan interaksi yang memodifikasi list (tambah, hapus, re-order item) dan pastikan UI ter-update dengan benar tanpa ada elemen yang "tertukar" atau tidak ter-render.
  3. Jika menggunakan React DevTools, periksa tab Profiler—re-render harus hanya terjadi pada item yang berubah, bukan seluruh list.

Perbaikan Permanen

Memberikan key secara manual setiap kali menulis .map() adalah langkah korektif, tapi bagaimana memastikan error ini tidak kembali muncul di masa depan? Berikut konfigurasi dan best practice yang membentengi codebase kamu:

Konfigurasi ESLint Rule

Pasang rule ESLint react/jsx-key yang secara otomatis mendeteksi penggunaan .map() tanpa key:

npm install --save-dev eslint eslint-plugin-react

Tambahkan konfigurasi berikut di .eslintrc.json:

{
  "plugins": ["react"],
  "rules": {
    "react/jsx-key": "error"
  }
}

Dengan konfigurasi ini, ESLint akan menampilkan error (bukan warning) setiap kali ada iterasi array yang merender JSX tanpa key. Ini mencegah kode bermasalah masuk ke codebase sekalipun developer lupa. Integrasi ke CI/CD pipeline memastikan bahwa PR yang melanggar rule ini gagal di-build, menjadikan proteksi ini permanen.

Best Practice: Desain Data dengan ID Sejak Awal

Pastikan setiap entity yang akan di-render sebagai list sudah memiliki field identifier unik sejak level data model. Jika data berasal dari API backend, koordinasikan dengan tim backend agar setiap response list menyertakan field id. Jika data dibuat di frontend (misalnya form entries yang belum disimpan), generate ID menggunakan library seperti uuid atau crypto.randomUUID():

import { v4 as uuidv4 } from 'uuid';

const newTodo = {
  id: uuidv4(),
  title: inputTitle,
  completed: false,
};

jadi, setiap item selalu punya ID stabil sejak penciptaannya, dan kamu tidak pernah tergoda menggunakan index sebagai key.

Best Practice: Hindari Index Sebagai Key Secara Total

Ada satu pengecualian di mana index sebagai key acceptable: ketika list bersifat static, read-only, dan tidak pernah di-reorder atau difilter. Namun sebagai aturan praktis, lebih baik menganggap index sebagai key itu selalu salah. Mentalitas ini mengeliminasi edge case dan membuat kode lebih robust terhadap perubahan future requirement.

Perbedaan Antar OS

Tidak ada perbedaan perilaku error ini antara Windows, macOS, atau Linux. Warning ini dihasilkan oleh React runtime di browser, yang berperilaku identik lintas platform. Namun, untuk setup ESLint, pastikan versi Node.js yang digunakan konsisten di seluruh OS tim development agar rule react/jsx-key berjalan seragam. Di Windows, path konfigurasi ESLint terkadang perlu penanganan khusus jika menggunakan backslash—gunakan eslint-use-flat-config atau format path secara normalisasi.

Kalau Masih Belum Berhasil

Kalau solusi di atas tidak work

Jika kamu sudah menambahkan key tapi warning masih muncul, kemungkinan besar ada lebih dari satu titik yang bermasalah. React hanya mencetak satu warning per komponen, jadi memperbaiki satu titik tidak menghilangkan warning dari titik lain di komponen yang sama atau komponen berbeda. Lakukan pencarian menyeluruh:

grep -rn "\.map(" src/ --include="*.jsx" --include="*.tsx"

Periksa setiap hasil untuk memastikan key ada. Juga periksa penggunaan .forEach() yang diikuti push ke array, lalu array di-render—pattern ini juga membutuhkan key tapi sering terlewat karena tidak langsung menggunakan .map().

Kasus sulit lainnya: komponen anak yang menerima children berupa list. Jika parent me-render <Parent>{items.map(...)}</Parent>, maka key harus diletakkan di elemen return map, bukan di <Parent>. Developer kadang salah menaruh key di wrapper parent padahal yang di-iterate adalah children-nya.

Cara baca log

K