DEV Community

Saba beigi
Saba beigi

Posted on

🧩 Building a Dynamic Accordion Table in React with TanStack Table & MUI

When it comes to presenting hierarchical data in a structured and interactive way, combining accordion layouts with data tables can be an elegant solution. Recently, I built a reusable AccordionTable component using React, TanStack Table (React Table v8), and Material UI (MUI) — and I’d like to walk you through how it works.

🎯 The Goal

  • I wanted a flexible table component that:
  • Displays summary rows that can be expanded to show child rows.
  • Supports skeleton loading states while fetching data.
  • Has consistent column widths across parent and child tables.
  • Uses MUI’s clean UI for styling and accessibility. This pattern works great for reports like balance sheets, financial statements, or nested datasets where each parent row represents a summarized entity.

⚙️ Core Technologies

  • React + TypeScript for component logic and type safety.
  • TanStack Table v8 for data modeling and rendering flexibility.
  • Material UI (MUI) for layout, styling, and accordion behavior.

🧱 Component Overview

export default function AccordionTable<T extends { id?: string; children?: any[] }>({
  data,
  columns,
  skeleton,
}: AccordionTableProps<T>) { ... }
Enter fullscreen mode Exit fullscreen mode

The component accepts:

  • data: an array of objects (each can optionally include children).
  • columns: TanStack column definitions.
  • skeleton: optional loading configuration (show and rows). It then renders a table where each row is wrapped inside an accordion. When expanded, it displays a nested ChildTable for the row’s children.

🧩 Handling Nested Data
To handle the child rows, I created a small reusable component:

function ChildTable({ rows, columns, columnWidths }: {
  rows: T[];
  columns: ColumnDef<T>[];
  columnWidths: string[];
}) {
  const childTable = useReactTable({
    data: rows,
    columns,
    getCoreRowModel: getCoreRowModel(),
  });

  return (
    <Table size="small">
      <TableBody>
        {childTable.getRowModel().rows.map((childRow) => (
          <TableRow key={childRow.id}>
            {childRow.getVisibleCells().map((cell, i) => (
              <TableCell key={cell.id} sx={{ width: columnWidths[i] }}>
                <Typography variant="bodyMdRegular" sx={{ color: "#696563" }}>
                  {flexRender(cell.column.columnDef.cell, cell.getContext())}
                </Typography>
              </TableCell>
            ))}
          </TableRow>
        ))}
      </TableBody>
    </Table>
  );
}
Enter fullscreen mode Exit fullscreen mode

This nested table keeps the same column structure as the main one and aligns neatly using predefined columnWidths.

🎨 Accordion Layout with MUI
Each main row becomes an MUI Accordion, with summary and details sections:

<Accordion>
  <AccordionSummary expandIcon={<ExpandMoreIcon />}>
    {/* Summary cells */}
  </AccordionSummary>
  <AccordionDetails>
    {/* Nested ChildTable */}
  </AccordionDetails>
</Accordion>

Enter fullscreen mode Exit fullscreen mode
  • The summary displays the top-level values (like category totals).
  • The details section renders the child rows (like subcategories). This approach keeps the UI compact and interactive while maintaining clarity.

💀 Skeleton Loading State
Before data loads, the component renders a lightweight placeholder using MUI’s :

{showSkeleton &&
  Array.from({ length: skeleton.rows }).map((_, index) => (
    <TableRow key={index}>
      {Array.from({ length: table.getAllColumns().length }).map((_, colIndex) => (
        <TableCell key={colIndex}><Skeleton /></TableCell>
      ))}
    </TableRow>
  ))}

Enter fullscreen mode Exit fullscreen mode

This improves perceived performance and maintains layout consistency during loading.

🧠 Key Learnings

  1. TanStack Table’s flexRender() makes it easy to render custom cell components.
  2. MUI Accordion integrates smoothly with table rows if you manage borders and padding carefully.
  3. Consistent column widths are crucial — define them in constants and reuse across parent and child tables.

🚀 Final Thoughts

The AccordionTable component combines the power of data tables with the clarity of hierarchical UI patterns. It’s especially useful for complex data structures like:

  • Financial reports (Balance Sheet, P&L, etc.)
  • Nested analytics results
  • Hierarchical user or asset data This approach keeps things modular, scalable, and visually clear while maintaining accessibility and responsiveness.

Top comments (0)