Table
To get the best performance we are using react-table
behind the scenes with shadcn DataTable
if you ever need more in depth examples follow their docs.
- Docs for react-table
- Docs for data-table
Table Types
Static Table
- Define Table Columns
- Create a folder
_components
page under/src/app/dashboard/page-name
- Create a file called
columns.tsx
- Create a folder
/src/app/dashboard/page-name/_components/columns.tsx
"use client";
import { ColumnDef } from "@tanstack/react-table";
// Example User type
type User = { id: string; name: string; email: string };
export const columns: ColumnDef<User>[] = [
{
// the object key of User type object
accessorKey: "id",
// (optional) the title of the header (returns value or react component)
header: "ID",
// (optional) the size of the column in pixels
size: 80,
},
{
// the object key of User type object
accessorKey: "name",
// (optional) the title of the header (returns value or react component)
header: "Name",
// (optional) use the cell property to modify the value of the key name
// (returns value or react component)
cell: ({ row: { original } }) => (
<p className="text-xs">{original.name.toUpperCase()}</p>
),
},
{
// the object key of User type object
accessorKey: "email",
// (optional) the title of the header (returns value or react component)
header: "Email",
},
];
- Create a table using
react-table
also under_components
/src/app/dashboard/page-name/_components/table.tsx
"use client";
import { DataTable } from "components/table/data-table";
import { columns } from "./columns";
import { getCoreRowModel, useReactTable } from "@tanstack/react-table";
// List of users
const data: User[] = [
{ id: 0, name: "Joe", email: "joe@email.com" },
{ id: 1, name: "Dave", email: "dave@email.com" },
];
const Table = () => {
// create the react-table using useReactTable hook
// pass the data, columns, getCoreRowModel
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
});
return (
<DataTable
// pass the table object here
table={table}
// accepts a flag to show a spinner instead of data
isFetching={false}
// navigate to a specific path on row click
// add the key you want to use in the User object between brackets in this case "/dashboard/users/[id]"
// it also accepts dot notation key { user: { id: 0 } } -> "/dashboard/users/[user.id]"
navigateTo="/dashboard/users/[id]"
/>
);
};
export default Table;
- Use the table in your
page.tsx
/src/app/dashboard/page-name/page.tsx
import Table from "./_components/table";
const ExamplePage = () => {
return <Table />;
};
export default ExamplePage;
Filterable Table
- Create only
columns.tsx
as you did before - Create
query-fields.tsx
in_components
/src/app/dashboard/page-name/_components/query-fields.tsx
"use client";
import DataTableQueryFields from "components/table/data-table-query-fields";
import { useFilteredTableContext } from "components/table/filtered-data-table";
import { Input } from "components/ui/input";
import { useSearchParams } from "next/navigation";
const QueryFields = () => {
const searchParams = useSearchParams();
// queryChangeHandler updates the searchParams
const { queryChangeHandler } = useFilteredTableContext();
return (
// DataTableQueryFields is just styled element
<DataTableQueryFields>
{/* Add an input element */}
<Input
placeholder="name"
// make sure to decode the URI so we don't get encoded characters
defaultValue={decodeURIComponent(searchParams.get("name") || "")}
// listen onChange to update the query when you type
onChange={(e) => queryChangeHandler({ id: e.target.value })}
/>
</DataTableQueryFields>
);
};
export default QueryFields;
- In your
page.tsx
/src/app/dashboard/page-name/page.tsx
import { Prisma } from "@prisma/client";
import FilteredDataTable from "components/table/filtered-data-table";
import { db } from "lib/db";
import { PropsWithParams } from "types";
import { FC } from "react";
import { columns } from "./_components/columns";
import QueryFields from "./_components/query-fields";
const Reviews: FC<PropsWithParams> = async ({ searchParams }) => {
const searchParams = await searchParams;
const {
// add default values to all keys
name = "",
// limit and page are added automatically to searchParams
limit = 10,
page = 0,
} = searchParams;
// Create a unified where input to use to count the rows and the find query
const where: Prisma.UserWhereInput = { name };
const rowCount = await db.productReview.count({ where });
// make sure page and limit are number
const pageAsNumber = Number(page);
const limitAsNumber = Number(limit);
// fetch the users
const users = await db.user.findMany({
where,
// important for pagination
skip: pageAsNumber * limitAsNumber,
take: pageAsNumber <= 0 ? limitAsNumber : pageAsNumber * limitAsNumber,
});
return (
<FilteredDataTable columns={columns} data={reviews} rowCount={rowCount}>
<ReviewsQueryFields />
</FilteredDataTable>
);
};
export default Reviews;