Objective

Understand the root cause of the "Each child in a list should have a unique key prop" warning raised when using a React Fragment and eliminate it.

Background

For a while now, I've ignored a React warning in this site's archive page code when rendering a list of historic posts. Given the issue raised is a warning, I deprioritized it. But, the reoccurrence of the warning finally got to me and I decided to explore why it was happening and how to fix it.

Here's the warning:

Warning: Each child in a list should have a unique "key" prop. See https://reactjs.org/link/warning-keys for more information. at PostsLists (webpack-internal: ///./components/posts-lists.js:10:22)

The first thing I did was to clean up the code to isolate the offending fragment. The code used several nested fragments.

Here's the cleaned up code:

import PostListItem from './post-listitem'

export default function PostsList({listTitle, posts, years, month}) {
  if (!posts || !Array.isArray(posts)) return null;
  return (  
    <>
      {
        years.map((year, y) => {
          return (
          <>
          <h3 key={y} className="text-lg font-bold mb-1" >{year}{(month)?" / " + month:""}</h3>           
          {          
            posts.map((post, i) => {
              if (post.year == year)
              return (
                <PostListItem key={i} post={post} />             
              )
              return null
            })            
          }
          </>
          )
        })
      }
    </>
  )
}

Root Cause and Solution

It turns out my mistake was using a shorthand React Fragment (<>) and adding a key prop to the wrong HTML element while mapping years. I originally added the shorthand Fragment because that map returns multiple components and I did not want to add an additional div to the resulting HTML.

Here's the revised code. Note that I had to import React here to use the full Fragment syntax and that the React.Fragment now carries the key prop instead of the H3 element.

import PostListItem from './post-listitem'
import React from 'react';

export default function PostsList({listTitle, posts, years, month}) {
  if (!posts || !Array.isArray(posts)) return null;
  return (  
    <>
      {
        years.map((year, y) => {
          return (
          <React.Fragment key={y}>
          <h3 className="text-lg font-bold mb-1" >{year}{(month)?" / " + month:""}</h3>           
          {          
            posts.map((post, i) => {
              if (post.year == year)
              return (
                <PostListItem key={i} post={post} />             
              )
              return null
            })            
          }
          </React.Fragment>
          )
        })
      }
    </>
  )
}

Helpful References