<tbody> cannot appear as a child of <div> and <div> cannot appear as a child of <table>

177 Views Asked by At

Please take a look at the schematic structure of my table. I removed all unnecessary stuff for ease of reading. As you can see, there is a header and there is a body. Only the body is wrapped in ScrollArea (https://www.radix-ui.com/primitives/docs/components/scroll-area), since I want the user to be able to scroll only the body and not the entire table

<table>
 <thead>
  <tr>
    <th>Name</th>
    <th>Surname</th>
    <th>City</th>
  </tr>
 </thead>
 <ScrollArea.Root>
  <ScrollArea.Viewport>
   {data.map((person) => (
     <tbody>
      <tr>
       <td>{person.name}</td>
       <td>{person.surname}</td>
       <td>{person.city}</td>
      </tr>
     </tbody>
  ))}
  </ScrollArea.Viewport>
  <ScrollArea.Scrollbar/>
 </ScrollArea.Root>
</table>

And so, when I go to the page with this table, I receive two warnings in the console:

Warning: validateDOMNesting(...): div cant appear as a child of table.

Warning: validateDOMNesting(...): tbody cannot appear as a child of div.

If I remove ScrollArea from the table, then the warnings disappear. But ScrollArea is very important to me for moving through long tables.

Tell me how can I get rid of these warnings?

3

There are 3 best solutions below

0
On BEST ANSWER

I want the user to be able to scroll only the body and not the entire table

It can be done with simple CSS as well using position: sticky:

.wrap {
  height: 80vh;
  overflow-y: scroll;
}

table {
  position: relative;
  width: 100%;
}
thead {
  position: sticky;
  top: 0px;
  
  background-color: pink;
  height: 50px;
}
td {
  height: 200px;
  text-align: center;
}

tr:nth-child(2n) {
  background-color: #eee;
}
<p>a table with sticky header</p>
<div class=wrap>
<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Surname</th>
      <th>City</th>
    </tr>
  </thead>
  <tbody>
   <!-- {data.map((person) => ( -->
    <tr><td>name1</td><td>surname1</td><td>city1</td></tr>
    <tr><td>name2</td><td>surname2</td><td>city2</td></tr>
    <tr><td>name3</td><td>surname3</td><td>city3</td></tr>
    <tr><td>name4</td><td>surname4</td><td>city4</td></tr>
    <tr><td>name5</td><td>surname5</td><td>city5</td></tr>
    <tr><td>name6</td><td>surname6</td><td>city6</td></tr>
   <!-- ))} -->
  </tbody>
</table>
<div>

4
On

You may want to break down your table into smaller components and then wrap those smaller components with <ScrollArea> tags when applicable.

What you would want to do is separate your code into two different files:

(table.js)

import React from 'react';
import * as ScrollArea from '@radix-ui/react-scroll-area';
import './styles.css';

import scrollArea_tbody from '../components/scrollArea_tbody';

const table = () => (
<table>
 <thead>
  <tr>
    <th></th>
    <th></th>
  </tr>
 </thead>
 <scrollArea_tbody />
</table>
);

export default table;

(scrollArea_tbody.js)

import React from 'react';
import * as ScrollArea from '@radix-ui/react-scroll-area';
import './styles.css';

const scrollArea_tbody = () => (
 <ScrollArea.Root>
  <ScrollArea.Viewport>
   <tbody>
    <tr>
     <td></td>
     <td></td>
    </tr>
   </tbody>
  </ScrollArea.Viewport>
  <ScrollArea.Scrollbar/>
 </ScrollArea.Root>
);

export default ScrollAreaDemo;

To build a component, I would follow along with the React documentation; specifically starting here would be a good call: https://react.dev/learn/your-first-component

2
On

By studying how table and ScrollArea work, I managed to find a solution on my own. The essence of the solution turned out to be to place thead and tbody in different tables

<>
 <table>
  <thead>
   <tr>
    <th>Name</th>
    <th>Surname</th>
    <th>City</th>
   </tr>
  </thead>
 </table>

 <ScrollArea.Root>
  <ScrollArea.Viewport>
   <table>
   {data.map((person) => (
     <tbody>
      <tr>
       <td>{person.name}</td>
       <td>{person.surname}</td>
       <td>{person.city}</td>
      </tr>
     </tbody>
  ))}
   </table>
  </ScrollArea.Viewport>
  <ScrollArea.Scrollbar/>
 </ScrollArea.Root>
</>