Shadcn Datatable/React Table pass additional data to columns
Answered
Eastern Phoebe posted this in #help-forum
Eastern PhoebeOP
I'm currently working on a Next.js project where I'm utilizing React Table to create a DataTable component. The column definitions are located in a separate file called
Here's a simplified version of the relevant code:
As mentioned above I don't habe any errors in my Editor (Visual Studio Code) but in the browser (see attached screenshot). Im using Nextjs 14.
Columns.tsx. While I can successfully pass data to the DataTable component in my page.tsx file, I've encountered a browser error that's not reflected in the editor.Here's a simplified version of the relevant code:
page.tsx// ... (imports)
export default async function Page() {
const { orgId } = auth()
const projects = await db.project.findMany({
// ... (database queries)
})
const customerEmails = await db.customer.findMany({
// ... (database queries)
})
return (
<>
{orgId ? (
<main>
<Container className="h-[80vh] flex justify-center items-center">
{/* ... (other UI components) */}
<div className="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
{/* I want to pass customerEmails to the columns file here */}
<DataTable columns={columns({ customerEmails })} data={projects} />
</div>
{/* ... (other UI components) */}
</Container>
</main>
) : (
// ... (error handling UI components)
)}
</>
);
}Columns.tsx// ... (imports)
type ProjectColumns = Omit<Project, "customerId"> & {
customer: {
email: string;
};
}
export const columns = ({ customerEmails }: { customerEmails: { email: string }[] }): ColumnDef<ProjectColumns>[] => [
// ... (column definitions)
];As mentioned above I don't habe any errors in my Editor (Visual Studio Code) but in the browser (see attached screenshot). Im using Nextjs 14.
41 Replies
Forest bachac
Just do columns={columns} no? You export columns as a const in columns.tsx so you don't need to call it as a function broski
Eastern PhoebeOP
Hi @Forest bachac , the problem is that I want to pass additional data from the database to the columns.tsx so I can work with it in the "Actions" column.
@Eastern Phoebe Hi <@578670669970276393> , the problem is that I want to pass additional data from the database to the columns.tsx so I can work with it in the "Actions" column.
Forest bachac
I think you have got the answer in your response - Do that in columns.tsx then? Is that possible
@Forest bachac I think you have got the answer in your response - Do that in columns.tsx then? Is that possible
Eastern PhoebeOP
Columns.tsx is a Client Component. As far as i know i can only get the prisma database data in a Servercomponent and pass it down to the client component.
I think it should be an array?
@Ray I think it should be an array?
Eastern PhoebeOP
so Column.tsx is marked with "use client" at the top and i used the datatable from shadcnui. my problem is that i have another client component (EditProjectDialog) where i need to pass extra data from the database. it looks like the following:
"use client"
export const columns = ({ customerEmails }: { customerEmails: { email: string }[] }): ColumnDef<ProjectColumns>[] => [
{
accessorKey: "name",
header: "Name",
},
{
accessorKey: "customer",
accessorFn: (row) => row.customer.email,
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Customer
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
)
},
},
{
accessorKey: "description",
header: "Description",
},
{
accessorKey: "status",
header: "Status",
},
{
id: "actions",
cell: async ({ row }) => {
const project = row.original
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">Open menu</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<EditProjectDialog project={project} customerEmails={customerEmails} /> // HERE IM PASSING THE CUSTOMEREMAILS I WANT TO PASS DOWN FROM THE PAGE.TSX
<DropdownMenuSeparator />
<DeleteCustomerDialog email={project.name} />
</DropdownMenuContent>
</DropdownMenu>
)
},
},
]@Ray and make the dropdown menu a client component
Eastern PhoebeOP
i think it need to be a client component this is the minimal example i found on shadcnui:
"use client"
import { ColumnDef } from "@tanstack/react-table"
// This type is used to define the shape of our data.
// You can use a Zod schema here if you want.
export type Payment = {
id: string
amount: number
status: "pending" | "processing" | "success" | "failed"
email: string
}
export const columns: ColumnDef<Payment>[] = [
{
accessorKey: "status",
header: "Status",
},
{
accessorKey: "email",
header: "Email",
},
{
accessorKey: "amount",
header: "Amount",
},
]can you pass
customerEmails to DataTable component?Answer
then in the cell map do this
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, {
...cell.getContext(),
customerEmails,
})}
</TableCell>
))}then you should be able to access it from the cell
{
id: "action",
cell(props) {
console.log(props.customerEmails)
},
},and change
columns() back to columnsthis is for the type
declare module "@tanstack/react-table" {
interface CellContext<TData extends unknown, TValue> {
customerEmails: string[];
}
}@Ray can you pass `customerEmails` to `DataTable` component?
Eastern PhoebeOP
Hi @Ray , sorry for the late response. I've implemented your code and it worked. Thanks a lot. But I had some problems with the type. My types file now looks like this:
Columns.tsx:
Datatable.tsx:
My code works perfectly fine now but I think theres a better solution for the types. Sadly I'm not a Typescript Expert. 🤔
import { CellContext, HeaderContext } from "@tanstack/react-table";
type CustomCellContext<TData, TValue> = {
customerEmails: { email: string }[];
} & CellContext<TData, TValue>;
type CustomHeaderContext<TData, TValue> = {
customerEmails: { email: string }[];
} & HeaderContext<TData, TValue>;Columns.tsx:
cell: ((props: CellContext<ProjectColumns, unknown> & { customerEmails: { email: string }[] }) => {
// some code
}Datatable.tsx:
...
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext() as CustomHeaderContext<TData, TValue> // aded type
)}
</TableHead>
...
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, {
...cell.getContext(),
customerEmails,
} as CustomCellContext<TData, TValue>)} // added type
</TableCell>
))}
</TableRow>
...My code works perfectly fine now but I think theres a better solution for the types. Sadly I'm not a Typescript Expert. 🤔
@Ray have you tried this?
Eastern PhoebeOP
Yeah tried that but I had multiple type erros. I also tried it with {email: string}[] which is the correct type for customerEmails
@Ray is customerEmails an array of string?
Eastern PhoebeOP
its
customerEmails: {email: string}[] I'm 100% surecan you show the error you got?
@Ray can you show the error you got?
Eastern PhoebeOP

Eastern PhoebeOP
ignore the commented code (its my type-chaos that worked)
declare module "@tanstack/react-table" {
interface CellContext<TData extends unknown, TValue> {
customerEmails: { email: string }[];
}
}this should work
@Ray ts
declare module "@tanstack/react-table" {
interface CellContext<TData extends unknown, TValue> {
customerEmails: { email: string }[];
}
}
this should work
Eastern PhoebeOP
Hmm i got the same code in my
types/tanstacktable.d.ts but i still got the typescript errors. Am I missing something like imports or some npm package?@Ray try putting this in the `Columns.tsx`
Eastern PhoebeOP
the errors still remain
what do you have in
types/tanstacktable.d.tsI got this code without error
//data-table.tsx
"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[];
}
export function DataTable<TData, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) {
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
state: {},
});
return (
<div className="rounded-md border">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
);
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, {
...cell.getContext(),
hello: "123",
})}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
);
}// page.tsx
import { Payment, columns } from "./columns";
import { DataTable } from "./data-table";
async function getData(): Promise<Payment[]> {
// Fetch data from your API here.
return [
{
id: "728ed52f",
amount: 100,
status: "pending",
email: "m@example.com",
},
{
id: "489e1d42",
amount: 125,
status: "processing",
email: "example@gmail.com",
},
// ...
];
}
export default async function DemoPage() {
const data = await getData();
return (
<div className="container mx-auto py-10">
<DataTable columns={columns} data={data} />
</div>
);
}// columns.tsx
"use client";
import { ColumnDef, CellContext } from "@tanstack/react-table";
import { Button } from "react-aria-components";
export type Payment = {
id: string;
amount: number;
status: "pending" | "processing" | "success" | "failed";
email: string;
};
declare module "@tanstack/react-table" {
interface CellContext<TData extends unknown, TValue> {
hello: string;
}
}
export const columns: ColumnDef<Payment>[] = [
{
accessorKey: "status",
header: "Status",
},
{
accessorKey: "email",
header: "Email",
},
{
accessorKey: "amount",
header: "Amount",
},
{
id: "action",
cell(props: CellContext<Payment, unknown>) {
return <Button>{props.hello}</Button>;
},
},
];@Ray what do you have in `types/tanstacktable.d.ts`
Eastern PhoebeOP
I just uncommented this module code in my
tanstacktable.d.ts file and somehow the errors disappeared I dont get why but i thank you so much.declare module "@tanstack/react-table" {
interface CellContext<TData extends unknown, TValue> {
customerEmails: {email: string}[];
}
}np glad you fixed it