Published on

Mixing shadcn/ui Data-Table and Sheet Components in Next.js

Authors
  • Name
    Ripal & Zalak
    Twitter

How to Mix shadcn/ui Data-Table and Sheet Components in Next.js

When building interactive and functional user interfaces, combining components like shadcn/ui Data-Table and Sheet can enhance usability. This guide explains how to integrate these components effectively in your Next.js application.

Problem Overview

While integrating the Data-Table component with a Sheet triggered on row click, developers often encounter layout issues where data renders incorrectly (e.g., collapsing into one column). This guide resolves such problems by:

  1. Structuring the Data-Table.
  2. Handling Sheet state effectively.
  3. Ensuring proper component interaction.

Prerequisites

  • Next.js application setup.
  • Installed shadcn/ui components.
  • Familiarity with React Table and Radix UI components.
  • Basic Tailwind CSS knowledge.

Implementation Steps

1. Data-Table Component Setup

First, create a DataTable component with required props, state management, and proper event handling for row clicks.

'use client'

import { ColumnDef, flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table'

import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from '@/components/ui/table'

interface DataTableProps<TData, TValue> {
  columns: ColumnDef<TData, TValue>[]
  data: TData[]
  onRowClick?: (row: TData) => void
}

export function DataTable<TData, TValue>({
  columns,
  data,
  onRowClick,
}: DataTableProps<TData, TValue>) {
  const table = useReactTable({
    data,
    columns,
    getCoreRowModel: getCoreRowModel(),
  })

  return (
    <div className="rounded-md border">
      <Table>
        <TableHeader>
          {table.getHeaderGroups().map((headerGroup) => (
            <TableRow key={headerGroup.id}>
              {headerGroup.headers.map((header) => (
                <TableHead key={header.id}>
                  {header.isPlaceholder
                    ? null
                    : flexRender(header.column.columnDef.header, header.getContext())}
                </TableHead>
              ))}
            </TableRow>
          ))}
        </TableHeader>
        <TableBody>
          {table.getRowModel().rows.map((row) => (
            <TableRow
              key={row.id}
              onClick={() => onRowClick && onRowClick(row.original)}
              className="cursor-pointer"
            >
              {row.getVisibleCells().map((cell) => (
                <TableCell key={cell.id}>
                  {flexRender(cell.column.columnDef.cell, cell.getContext())}
                </TableCell>
              ))}
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </div>
  )
}

2. Parent Component with Sheet State

Handle the Sheet visibility and row data in the parent component:

import { useState } from 'react'
import { DataTable } from '@/components/DataTable'
import {
  Sheet,
  SheetContent,
  SheetHeader,
  SheetTitle,
  SheetDescription,
} from '@/components/ui/sheet'

export default function Page() {
  const [isSheetOpen, setIsSheetOpen] = useState(false)
  const [rowDetails, setRowDetails] = useState(null)

  const handleRowClick = (row) => {
    setIsSheetOpen(true)
    setRowDetails(row)
  }

  const columns = [
    { accessorKey: 'id', header: 'ID' },
    { accessorKey: 'name', header: 'Name' },
    { accessorKey: 'email', header: 'Email' },
  ]

  const data = [
    { id: 1, name: 'John Doe', email: '[email protected]' },
    { id: 2, name: 'Jane Smith', email: '[email protected]' },
  ]

  return (
    <div>
      <DataTable columns={columns} data={data} onRowClick={handleRowClick} />
      <Sheet open={isSheetOpen} onOpenChange={setIsSheetOpen}>
        <SheetContent>
          <SheetHeader>
            <SheetTitle>Row Details</SheetTitle>
            <SheetDescription>
              {rowDetails && (
                <div>
                  <p>ID: {rowDetails.id}</p>
                  <p>Name: {rowDetails.name}</p>
                  <p>Email: {rowDetails.email}</p>
                </div>
              )}
            </SheetDescription>
          </SheetHeader>
        </SheetContent>
      </Sheet>
    </div>
  )
}

Key Takeaways

  • Define onRowClick in the Data-Table for custom row actions.
  • Use Sheet in the parent component to display additional data.
  • Pass row data through state for dynamic content.

FAQs

1. Why is my data collapsing into a single column? Ensure that the flexRender and column definitions in your Data-Table are correctly structured and align with your data.

2. How can I style the table and sheet components? Use TailwindCSS classes for customization or extend the shadcn/ui styles.

3. Can I use this approach with server-side data? Yes, fetch your data in getServerSideProps or getStaticProps and pass it as props to the components.

Conclusion

By combining shadcn/ui Data-Table and Sheet, you can build dynamic, user-friendly interfaces in Next.js. Use the above patterns to enhance your project and resolve common rendering issues.