Custom Shadcn Data Grid for React and Tailwind CSS. A powerful TanStack Table v8 data grid with sorting, filtering, pagination, footer rows, drag-and-drop, virtualization, infinite scroll, and row pinning.
The current data-grid package ships the shared grid context, table renderers, pagination, column controls, drag-and-drop helpers, virtualization, infinite scroll, footer helpers, and row-pinning support. Footer components (DataGridTableFoot, DataGridTableFootRow, DataGridTableFootRowCell) and the DataGridTableRowPin toggle are exported from data-grid-table.tsx.
In the base build, data-grid-scroll-area.tsx is also included and exports DataGridScrollArea for dedicated scroll handling around sticky-header or wide tables.
Browse 29 production-ready Shadcn Data Grid components for dashboards, forms, and product UI. These examples use Base UI primitives from @base-ui/react and stay fully compatible with Shadcn Create so radius, color, and typography match your configured theme.
Browse all 29 Shadcn Data Grid components for copy-ready layouts, dashboards, and forms built with Tailwind CSS in the ReUI library.
import {
DataGrid,
DataGridContainer,
DataGridPagination,
DataGridTable,
DataGridTableFootRow,
DataGridTableFootRowCell,
} from "@/components/reui/data-grid"const footer = (
<DataGridTableFootRow>
<DataGridTableFootRowCell colSpan={columns.length}>
Showing {data.length} rows
</DataGridTableFootRowCell>
</DataGridTableFootRow>
)
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
})
return (
<DataGrid
table={table}
recordCount={data.length}
tableLayout={{ rowsPinnable: true }}
>
<DataGridContainer>
<DataGridTable footerContent={footer} />
</DataGridContainer>
<DataGridPagination />
</DataGrid>
)Use DataGridTableRowPin inside a column definition to let users pin rows, swap in DataGridTableVirtual when you need virtualization or infinite scroll, and wrap sticky-header tables with DataGridScrollArea when you want the dedicated base scroll wrapper. When rows can be pinned or virtualized, provide a stable getRowId so row identity stays intact across reordering.
For the full set of variations — expandable rows, sub-grids, sortable / movable / draggable / resizable / pinnable columns, sticky header, column controls and visibility, loading skeleton, CRUD in frame, footer totals / summary / aggregates, infinite scroll, and row pinning — browse the Data Grid components.
The root component that provides the table context.
Configuration object for the table's behavior and appearance.
Custom CSS classes for different parts of the table.
A scrollable container for the table.
Dedicated scroll wrapper for wide grids and sticky headers.
The component that renders the actual HTML table. It automatically handles data rendering, loading states (skeletons/spinners), empty states, footer rows, and pinned rows when rowsPinnable is enabled on the parent DataGrid.
The component for table pagination controls.
A flexible column header component with built-in support for sorting, pinning, and moving columns.
A popover-based multi-select filter for columns.
A dropdown component to toggle column visibility.
Used for enabling column drag-and-drop reordering with optional footer rendering.
Used for enabling row drag-and-drop reordering with optional footer rendering.
A virtualized table renderer using @tanstack/react-virtual for row virtualization, infinite scroll, optional footer rows, and pinned rows when rowsPinnable is enabled. The wrapper manages row count and the scroll element for you, while virtualizerOptions lets you customize the underlying TanStack Virtual instance.
A pin/unpin toggle button for use in column definitions to enable row pinning.
Wrapper component for the table footer (<tfoot>).
A row inside the table footer.
A cell inside a footer row.
"use client"
import { useMemo, useState } from "react"
import {
DataGrid,
DataGridContainer,
} from "@/components/reui/data-grid/data-grid"
import { DataGridPagination } from "@/components/reui/data-grid/data-grid-pagination"
import { DataGridScrollArea } from "@/components/reui/data-grid/data-grid-scroll-area"
import { DataGridTable } from "@/components/reui/data-grid/data-grid-table"
import {
ColumnDef,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
PaginationState,
SortingState,
useReactTable,
} from "@tanstack/react-table"
const users = [
{
id: "1",
name: "Alex Johnson",
email: "alex@example.com",
avatar:
"https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=96&h=96&dpr=2&q=80",
initials: "AJ",
},
{
id: "2",
name: "Sarah Chen",
email: "sarah@example.com",
avatar:
"https://images.unsplash.com/photo-1519699047748-de8e457a634e?w=96&h=96&dpr=2&q=80",
initials: "SC",
},
{
id: "3",
name: "Michael Rodriguez",
email: "michael@example.com",
avatar:
"https://images.unsplash.com/photo-1584308972272-9e4e7685e80f?w=96&h=96&dpr=2&q=80",
initials: "MR",
},
{
id: "4",
name: "Emma Wilson",
email: "emma@example.com",
avatar:
"https://images.unsplash.com/photo-1485893086445-ed75865251e0?w=96&h=96&dpr=2&q=80",
initials: "EW",
},
{
id: "5",
name: "David Kim",
email: "david@example.com",
avatar:
"https://images.unsplash.com/photo-1607990281513-2c110a25bd8c?w=96&h=96&dpr=2&q=80",
initials: "DK",
},
{
id: "6",
name: "Aron Thompson",
email: "lisa@example.com",
avatar:
"https://images.unsplash.com/photo-1527980965255-d3b416303d12?w=96&h=96&dpr=2&q=80",
initials: "LT",
},
{
id: "7",
name: "James Brown",
email: "james@example.com",
avatar:
"https://images.unsplash.com/photo-1543299750-19d1d6297053?w=96&h=96&dpr=2&q=80",
initials: "JB",
},
{
id: "8",
name: "Maria Garcia",
email: "maria@example.com",
avatar:
"https://images.unsplash.com/photo-1620075225255-8c2051b6c015?w=96&h=96&dpr=2&q=80",
initials: "MG",
},
{
id: "9",
name: "Nick Johnson",
email: "nick@example.com",
avatar:
"https://images.unsplash.com/photo-1485206412256-701ccc5b93ca?w=96&h=96&dpr=2&q=80",
initials: "NJ",
},
{
id: "10",
name: "Liam Thompson",
email: "liam@example.com",
avatar:
"https://images.unsplash.com/photo-1542595913-85d69b0edbaf?w=96&h=96&dpr=2&q=80",
initials: "LT",
},
]
interface IData {
id: string
name: string
availability: "online" | "away" | "busy" | "offline"
avatar: string
status: "active" | "inactive"
flag: string // Emoji flags
email: string
company: string
role: string
joined: string
location: string
balance: number
}
const demoData: IData[] = users.map((user, index) => ({
...user,
availability: (["online", "away", "busy", "offline"] as const)[index % 4],
status: (index % 2 === 0 ? "active" : "inactive") as "active" | "inactive",
flag: (["us", "gb", "ca", "au", "de", "my", "es", "jp", "fr", "it"] as const)[
index % 10
],
company: (
[
"Apple",
"OpenAI",
"Meta",
"Tesla",
"SAP",
"Keenthemes",
"BBVA",
"Sony",
"LVMH",
"ENI",
] as const
)[index % 10],
role: (
[
"CEO",
"CTO",
"Designer",
"Developer",
"Lawyer",
"Director",
"Product Manager",
"Marketing Lead",
"Data Scientist",
"Engineer",
] as const
)[index % 10],
joined: "Jan, 2024",
location: (
[
"United States",
"United Kingdom",
"Canada",
"Australia",
"Germany",
"Malaysia",
"Spain",
"Japan",
"France",
"Italy",
] as const
)[index % 10],
balance: 5143.03 + index * 100,
}))
export function Pattern() {
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 5,
})
const [sorting, setSorting] = useState<SortingState>([
{ id: "name", desc: true },
])
const columns = useMemo<ColumnDef<IData>[]>(
() => [
{
accessorKey: "name",
header: "Name",
cell: (info) => <>{info.getValue() as string}</>,
size: 150,
meta: {
headerClassName: "",
cellClassName: "",
},
},
{
accessorKey: "email",
header: "Email",
cell: (info) => (
<div className="truncate">
<a
href={`mailto:${info.getValue()}`}
className="hover:text-primary truncate hover:underline"
>
{info.getValue() as string}
</a>
</div>
),
size: 150,
meta: {
headerClassName: "",
cellClassName: "",
},
},
{
accessorKey: "location",
header: "Location",
cell: ({ row }) => (
<div className="flex items-center gap-1.5">
<img
src={`https://flagcdn.com/${row.original.flag.toLowerCase()}.svg`}
alt={row.original.flag}
className="size-4 rounded-full object-cover"
/>
<div className="text-foreground">{row.original.location}</div>
</div>
),
size: 175,
meta: {
headerClassName: "",
cellClassName: "",
},
},
{
accessorKey: "balance",
header: "Balance ($)",
cell: (info) => <>${(info.getValue() as number).toFixed(2)}</>,
size: 100,
meta: {
headerClassName: "text-right rtl:text-left",
cellClassName: "text-right rtl:text-left",
},
},
],
[]
)
const table = useReactTable({
columns,
data: demoData,
pageCount: Math.ceil((demoData?.length || 0) / pagination.pageSize),
getRowId: (row: IData) => row.id,
state: {
pagination,
sorting,
},
onPaginationChange: setPagination,
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
})
return (
<DataGrid table={table} recordCount={demoData?.length || 0}>
<div className="w-full space-y-2.5">
<DataGridContainer>
<DataGridScrollArea>
<DataGridTable />
</DataGridScrollArea>
</DataGridContainer>
<DataGridPagination />
</div>
</DataGrid>
)
}
"use client"
import { useMemo, useState } from "react"
import {
DataGrid,
DataGridContainer,
} from "@/components/reui/data-grid/data-grid"
import { DataGridPagination } from "@/components/reui/data-grid/data-grid-pagination"
import { DataGridScrollArea } from "@/components/reui/data-grid/data-grid-scroll-area"
import { DataGridTable } from "@/components/reui/data-grid/data-grid-table"
import {
ColumnDef,
ColumnOrderState,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
PaginationState,
SortingState,
useReactTable,
} from "@tanstack/react-table"
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/components/ui/avatar"
import { Card } from "@/components/ui/card"
interface IData {
id: string
name: string
availability: "online" | "away" | "busy" | "offline"
avatar: string
status: "active" | "inactive"
flag: string // Emoji flags
email: string
company: string
role: string
joined: string
location: string
balance: number
}
const users = [
{
id: "1",
name: "Alex Johnson",
email: "alex@example.com",
avatar:
"https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=96&h=96&dpr=2&q=80",
initials: "AJ",
},
{
id: "2",
name: "Sarah Chen",
email: "sarah@example.com",
avatar:
"https://images.unsplash.com/photo-1519699047748-de8e457a634e?w=96&h=96&dpr=2&q=80",
initials: "SC",
},
{
id: "3",
name: "Michael Rodriguez",
email: "michael@example.com",
avatar:
"https://images.unsplash.com/photo-1584308972272-9e4e7685e80f?w=96&h=96&dpr=2&q=80",
initials: "MR",
},
{
id: "4",
name: "Emma Wilson",
email: "emma@example.com",
avatar:
"https://images.unsplash.com/photo-1485893086445-ed75865251e0?w=96&h=96&dpr=2&q=80",
initials: "EW",
},
{
id: "5",
name: "David Kim",
email: "david@example.com",
avatar:
"https://images.unsplash.com/photo-1607990281513-2c110a25bd8c?w=96&h=96&dpr=2&q=80",
initials: "DK",
},
{
id: "6",
name: "Aron Thompson",
email: "lisa@example.com",
avatar:
"https://images.unsplash.com/photo-1527980965255-d3b416303d12?w=96&h=96&dpr=2&q=80",
initials: "LT",
},
{
id: "7",
name: "James Brown",
email: "james@example.com",
avatar:
"https://images.unsplash.com/photo-1543299750-19d1d6297053?w=96&h=96&dpr=2&q=80",
initials: "JB",
},
{
id: "8",
name: "Maria Garcia",
email: "maria@example.com",
avatar:
"https://images.unsplash.com/photo-1620075225255-8c2051b6c015?w=96&h=96&dpr=2&q=80",
initials: "MG",
},
{
id: "9",
name: "Nick Johnson",
email: "nick@example.com",
avatar:
"https://images.unsplash.com/photo-1485206412256-701ccc5b93ca?w=96&h=96&dpr=2&q=80",
initials: "NJ",
},
{
id: "10",
name: "Liam Thompson",
email: "liam@example.com",
avatar:
"https://images.unsplash.com/photo-1542595913-85d69b0edbaf?w=96&h=96&dpr=2&q=80",
initials: "LT",
},
]
const demoData: IData[] = users.map((user, index) => ({
...user,
availability: (["online", "away", "busy", "offline"] as const)[index % 4],
status: (index % 2 === 0 ? "active" : "inactive") as "active" | "inactive",
flag: (["us", "gb", "ca", "au", "de", "my", "es", "jp", "fr", "it"] as const)[
index % 10
],
company: (
[
"Apple",
"OpenAI",
"Meta",
"Tesla",
"SAP",
"Keenthemes",
"BBVA",
"Sony",
"LVMH",
"ENI",
] as const
)[index % 10],
role: (
[
"CEO",
"CTO",
"Designer",
"Developer",
"Lawyer",
"Director",
"Product Manager",
"Marketing Lead",
"Data Scientist",
"Engineer",
] as const
)[index % 10],
joined: "Jan, 2024",
location: (
[
"United States",
"United Kingdom",
"Canada",
"Australia",
"Germany",
"Malaysia",
"Spain",
"Japan",
"France",
"Italy",
] as const
)[index % 10],
balance: 5143.03 + index * 100,
}))
export function Pattern() {
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 5,
})
const [sorting, setSorting] = useState<SortingState>([
{ id: "name", desc: true },
])
const [columnOrder, setColumnOrder] = useState<ColumnOrderState>([])
const columns = useMemo<ColumnDef<IData>[]>(
() => [
{
accessorKey: "name",
id: "name",
header: "Name",
cell: ({ row }) => {
return (
<div className="flex items-center gap-3">
<Avatar className="size-8">
<AvatarImage
src={row.original.avatar}
alt={row.original.name}
/>
<AvatarFallback>
{row.original.name
.split(" ")
.map((n) => n[0])
.join("")}
</AvatarFallback>
</Avatar>
<div className="space-y-px">
<div className="text-foreground font-medium">
{row.original.name}
</div>
<div className="text-muted-foreground">
{row.original.email}
</div>
</div>
</div>
)
},
size: 250,
enableSorting: true,
enableHiding: false,
},
{
accessorKey: "company",
header: "Company",
cell: (info) => <span>{info.getValue() as string}</span>,
size: 100,
meta: {
headerClassName: "",
},
},
{
accessorKey: "role",
header: "Occupation",
cell: (info) => <span>{info.getValue() as string}</span>,
size: 100,
meta: {
headerClassName: "",
},
},
{
accessorKey: "balance",
header: "Salary",
cell: (info) => (
<span className="font-semibold">
${(info.getValue() as number).toFixed(2)}
</span>
),
size: 100,
},
],
[]
)
const table = useReactTable({
columns,
data: demoData,
pageCount: Math.ceil((demoData?.length || 0) / pagination.pageSize),
getRowId: (row: IData) => row.id,
state: {
pagination,
sorting,
columnOrder,
},
onPaginationChange: setPagination,
onSortingChange: setSorting,
onColumnOrderChange: setColumnOrder,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
})
return (
<DataGrid
table={table}
recordCount={demoData?.length || 0}
tableLayout={{
cellBorder: true,
}}
>
<div className="w-full space-y-2.5">
<Card className="p-0">
<DataGridContainer>
<DataGridScrollArea>
<DataGridTable />
</DataGridScrollArea>
</DataGridContainer>
</Card>
<DataGridPagination />
</div>
</DataGrid>
)
}
"use client"
import { useMemo, useState } from "react"
import {
DataGrid,
DataGridContainer,
} from "@/components/reui/data-grid/data-grid"
import { DataGridPagination } from "@/components/reui/data-grid/data-grid-pagination"
import { DataGridScrollArea } from "@/components/reui/data-grid/data-grid-scroll-area"
import { DataGridTable } from "@/components/reui/data-grid/data-grid-table"
import {
ColumnDef,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
PaginationState,
SortingState,
useReactTable,
} from "@tanstack/react-table"
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/components/ui/avatar"
interface IData {
id: string
name: string
availability: "online" | "away" | "busy" | "offline"
avatar: string
status: "active" | "inactive"
flag: string // Emoji flags
email: string
company: string
role: string
joined: string
location: string
balance: number
}
const users = [
{
id: "1",
name: "Alex Johnson",
email: "alex@example.com",
avatar:
"https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=96&h=96&dpr=2&q=80",
initials: "AJ",
},
{
id: "2",
name: "Sarah Chen",
email: "sarah@example.com",
avatar:
"https://images.unsplash.com/photo-1519699047748-de8e457a634e?w=96&h=96&dpr=2&q=80",
initials: "SC",
},
{
id: "3",
name: "Michael Rodriguez",
email: "michael@example.com",
avatar:
"https://images.unsplash.com/photo-1584308972272-9e4e7685e80f?w=96&h=96&dpr=2&q=80",
initials: "MR",
},
{
id: "4",
name: "Emma Wilson",
email: "emma@example.com",
avatar:
"https://images.unsplash.com/photo-1485893086445-ed75865251e0?w=96&h=96&dpr=2&q=80",
initials: "EW",
},
{
id: "5",
name: "David Kim",
email: "david@example.com",
avatar:
"https://images.unsplash.com/photo-1607990281513-2c110a25bd8c?w=96&h=96&dpr=2&q=80",
initials: "DK",
},
{
id: "6",
name: "Aron Thompson",
email: "lisa@example.com",
avatar:
"https://images.unsplash.com/photo-1527980965255-d3b416303d12?w=96&h=96&dpr=2&q=80",
initials: "LT",
},
{
id: "7",
name: "James Brown",
email: "james@example.com",
avatar:
"https://images.unsplash.com/photo-1543299750-19d1d6297053?w=96&h=96&dpr=2&q=80",
initials: "JB",
},
{
id: "8",
name: "Maria Garcia",
email: "maria@example.com",
avatar:
"https://images.unsplash.com/photo-1620075225255-8c2051b6c015?w=96&h=96&dpr=2&q=80",
initials: "MG",
},
{
id: "9",
name: "Nick Johnson",
email: "nick@example.com",
avatar:
"https://images.unsplash.com/photo-1485206412256-701ccc5b93ca?w=96&h=96&dpr=2&q=80",
initials: "NJ",
},
{
id: "10",
name: "Liam Thompson",
email: "liam@example.com",
avatar:
"https://images.unsplash.com/photo-1542595913-85d69b0edbaf?w=96&h=96&dpr=2&q=80",
initials: "LT",
},
]
const demoData: IData[] = users.map((user, index) => ({
...user,
availability: (["online", "away", "busy", "offline"] as const)[index % 4],
status: (index % 2 === 0 ? "active" : "inactive") as "active" | "inactive",
flag: (["us", "gb", "ca", "au", "de", "my", "es", "jp", "fr", "it"] as const)[
index % 10
],
company: (
[
"Apple",
"OpenAI",
"Meta",
"Tesla",
"SAP",
"Keenthemes",
"BBVA",
"Sony",
"LVMH",
"ENI",
] as const
)[index % 10],
role: (
[
"CEO",
"CTO",
"Designer",
"Developer",
"Lawyer",
"Director",
"Product Manager",
"Marketing Lead",
"Data Scientist",
"Engineer",
] as const
)[index % 10],
joined: "Jan, 2024",
location: (
[
"United States",
"United Kingdom",
"Canada",
"Australia",
"Germany",
"Malaysia",
"Spain",
"Japan",
"France",
"Italy",
] as const
)[index % 10],
balance: 5143.03 + index * 100,
}))
export function Pattern() {
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 5,
})
const [sorting, setSorting] = useState<SortingState>([
{ id: "name", desc: true },
])
const columns = useMemo<ColumnDef<IData>[]>(
() => [
{
accessorKey: "name",
id: "name",
header: "Name",
cell: ({ row }) => {
return (
<div className="flex items-center gap-2">
<Avatar className="size-6">
<AvatarImage
src={row.original.avatar}
alt={row.original.name}
/>
<AvatarFallback>
{row.original.name
.split(" ")
.map((n) => n[0])
.join("")}
</AvatarFallback>
</Avatar>
<a
href="#"
className="text-foreground hover:text-primary font-medium"
>
{row.original.name}
</a>
</div>
)
},
size: 200,
enableSorting: true,
enableHiding: false,
},
{
accessorKey: "email",
header: "Email",
cell: (info) => (
<a
href={`mailto:${info.getValue()}`}
className="hover:text-primary hover:underline"
>
{info.getValue() as string}
</a>
),
size: 175,
meta: {
headerClassName: "",
},
},
{
accessorKey: "location",
header: "Location",
cell: ({ row }) => {
return (
<div className="flex items-center gap-1.5">
<img
src={`https://flagcdn.com/${row.original.flag.toLowerCase()}.svg`}
alt={row.original.flag}
className="size-4 rounded-full object-cover"
/>
<div className="text-foreground font-medium">
{row.original.location}
</div>
</div>
)
},
size: 175,
meta: {
headerClassName: "",
cellClassName: "text-start",
},
},
{
accessorKey: "balance",
header: "Balance ($)",
cell: (info) => (
<span className="font-semibold">
${(info.getValue() as number).toFixed(2)}
</span>
),
size: 125,
meta: {
headerClassName: "text-right rtl:text-left",
cellClassName: "text-right rtl:text-left",
},
},
],
[]
)
const table = useReactTable({
columns,
data: demoData,
pageCount: Math.ceil((demoData?.length || 0) / pagination.pageSize),
getRowId: (row: IData) => row.id,
state: {
pagination,
sorting,
},
onPaginationChange: setPagination,
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
})
return (
<DataGrid
table={table}
recordCount={demoData?.length || 0}
tableLayout={{ dense: true }}
>
<div className="w-full space-y-2.5">
<DataGridContainer>
<DataGridScrollArea>
<DataGridTable />
</DataGridScrollArea>
</DataGridContainer>
<DataGridPagination />
</div>
</DataGrid>
)
}
"use client"
import { useMemo, useState } from "react"
import { Badge } from "@/components/reui/badge"
import {
DataGrid,
DataGridContainer,
} from "@/components/reui/data-grid/data-grid"
import { DataGridPagination } from "@/components/reui/data-grid/data-grid-pagination"
import { DataGridScrollArea } from "@/components/reui/data-grid/data-grid-scroll-area"
import { DataGridTable } from "@/components/reui/data-grid/data-grid-table"
import {
ColumnDef,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
PaginationState,
SortingState,
useReactTable,
} from "@tanstack/react-table"
import { cn } from "@/lib/utils"
import {
Avatar,
AvatarBadge,
AvatarFallback,
AvatarImage,
} from "@/components/ui/avatar"
interface IData {
id: string
name: string
availability: "online" | "away" | "busy" | "offline"
avatar: string
status: "active" | "inactive"
flag: string // Emoji flags
email: string
company: string
role: string
joined: string
location: string
balance: number
}
const demoData: IData[] = [
{
id: "1",
name: "Alex Johnson",
availability: "online",
avatar:
"https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "us",
email: "alex@apple.com",
company: "Apple",
role: "CEO",
joined: "Jan, 2024",
location: "United States",
balance: 5143.03,
},
{
id: "2",
name: "Sarah Chen",
availability: "away",
avatar:
"https://images.unsplash.com/photo-1519699047748-de8e457a634e?w=96&h=96&dpr=2&q=80",
status: "inactive",
flag: "gb",
email: "sarah@openai.com",
company: "OpenAI",
role: "CTO",
joined: "Mar, 2023",
location: "United Kingdom",
balance: 4321.87,
},
{
id: "3",
name: "Michael Rodriguez",
availability: "busy",
avatar:
"https://images.unsplash.com/photo-1584308972272-9e4e7685e80f?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "ca",
email: "michael@meta.com",
company: "Meta",
role: "Designer",
joined: "Jun, 2022",
location: "Canada",
balance: 7654.98,
},
{
id: "4",
name: "Emma Wilson",
availability: "offline",
avatar:
"https://images.unsplash.com/photo-1485893086445-ed75865251e0?w=96&h=96&dpr=2&q=80",
status: "inactive",
flag: "au",
email: "emma@tesla.com",
company: "Tesla",
role: "Developer",
joined: "Sep, 2024",
location: "Australia",
balance: 3456.45,
},
{
id: "5",
name: "David Kim",
availability: "online",
avatar:
"https://images.unsplash.com/photo-1607990281513-2c110a25bd8c?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "de",
email: "david@sap.com",
company: "SAP",
role: "Lawyer",
joined: "Nov, 2023",
location: "Germany",
balance: 9876.54,
},
{
id: "6",
name: "Aron Thompson",
availability: "away",
avatar:
"https://images.unsplash.com/photo-1527980965255-d3b416303d12?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "my",
email: "aron@keenthemes.com",
company: "Keenthemes",
role: "Director",
joined: "Feb, 2022",
location: "Malaysia",
balance: 6214.22,
},
{
id: "7",
name: "James Brown",
availability: "busy",
avatar:
"https://images.unsplash.com/photo-1543299750-19d1d6297053?w=96&h=96&dpr=2&q=80",
status: "inactive",
flag: "es",
email: "james@bbva.es",
company: "BBVA",
role: "Product Manager",
joined: "Aug, 2024",
location: "Spain",
balance: 5321.77,
},
{
id: "8",
name: "Maria Garcia",
availability: "offline",
avatar:
"https://images.unsplash.com/photo-1620075225255-8c2051b6c015?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "jp",
email: "maria@sony.jp",
company: "Sony",
role: "Marketing Lead",
joined: "Dec, 2023",
location: "Japan",
balance: 8452.39,
},
{
id: "9",
name: "Nick Johnson",
availability: "online",
avatar:
"https://images.unsplash.com/photo-1485206412256-701ccc5b93ca?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "fr",
email: "nick@lvmh.fr",
company: "LVMH",
role: "Data Scientist",
joined: "Apr, 2022",
location: "France",
balance: 7345.1,
},
{
id: "10",
name: "Liam Thompson",
availability: "away",
avatar:
"https://images.unsplash.com/photo-1542595913-85d69b0edbaf?w=96&h=96&dpr=2&q=80",
status: "inactive",
flag: "it",
email: "liam@eni.it",
company: "ENI",
role: "Engineer",
joined: "Jul, 2024",
location: "Italy",
balance: 5214.88,
},
{
id: "11",
name: "Alex Johnson",
availability: "busy",
avatar:
"https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "br",
email: "alex@vale.br",
company: "Vale",
role: "Software Engineer",
joined: "May, 2023",
location: "Brazil",
balance: 9421.5,
},
{
id: "12",
name: "Sarah Chen",
availability: "offline",
avatar:
"https://images.unsplash.com/photo-1519699047748-de8e457a634e?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "in",
email: "sarah@tata.in",
company: "Tata",
role: "Sales Manager",
joined: "Oct, 2024",
location: "India",
balance: 4521.67,
},
]
export function Pattern() {
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 5,
})
const [sorting, setSorting] = useState<SortingState>([
{ id: "name", desc: true },
])
const columns = useMemo<ColumnDef<IData>[]>(
() => [
{
accessorKey: "name",
id: "name",
header: "Name",
cell: ({ row }) => {
const availability = row.original.availability
const statusColors = {
online: "bg-green-500",
away: "bg-yellow-500",
busy: "bg-orange-500",
offline: "bg-gray-400",
}
return (
<div className="flex items-center gap-3">
<Avatar className="size-8">
<AvatarImage
src={row.original.avatar}
alt={row.original.name}
/>
<AvatarFallback>
{row.original.name
.split(" ")
.map((n) => n[0])
.join("")}
</AvatarFallback>
<AvatarBadge
className={cn(
"size-1.5! p-0",
statusColors[availability] || statusColors.offline
)}
/>
</Avatar>
<div className="space-y-px">
<div className="text-foreground font-medium">
{row.original.name}
</div>
<div className="text-muted-foreground">
{row.original.email}
</div>
</div>
</div>
)
},
size: 225,
enableSorting: true,
enableHiding: false,
},
{
accessorKey: "location",
header: "Location",
cell: ({ row }) => {
return (
<div className="flex items-center gap-1.5">
<img
src={`https://flagcdn.com/${row.original.flag.toLowerCase()}.svg`}
alt={row.original.flag}
className="size-4 rounded-full object-cover"
/>
<div className="text-foreground font-medium">
{row.original.location}
</div>
</div>
)
},
size: 160,
meta: {
headerClassName: "",
cellClassName: "text-start",
},
},
{
accessorKey: "status",
id: "status",
header: "Status",
cell: ({ row }) => {
const status = row.original.status
if (status == "active") {
return <Badge variant="success-outline">Approved</Badge>
} else {
return <Badge variant="warning-outline">Pending</Badge>
}
},
size: 100,
},
],
[]
)
const table = useReactTable({
columns,
data: demoData,
pageCount: Math.ceil((demoData?.length || 0) / pagination.pageSize),
getRowId: (row: IData) => row.id,
state: {
pagination,
sorting,
},
onPaginationChange: setPagination,
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
})
return (
<DataGrid
table={table}
recordCount={demoData?.length || 0}
tableLayout={{
headerBackground: false,
rowBorder: false,
rowRounded: true,
}}
>
<div className="w-full space-y-2.5">
<DataGridContainer border={false}>
<DataGridScrollArea>
<DataGridTable />
</DataGridScrollArea>
</DataGridContainer>
<DataGridPagination />
</div>
</DataGrid>
)
}
"use client"
import { useMemo, useState } from "react"
import {
DataGrid,
DataGridContainer,
} from "@/components/reui/data-grid/data-grid"
import { DataGridPagination } from "@/components/reui/data-grid/data-grid-pagination"
import { DataGridScrollArea } from "@/components/reui/data-grid/data-grid-scroll-area"
import { DataGridTable } from "@/components/reui/data-grid/data-grid-table"
import {
ColumnDef,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
PaginationState,
SortingState,
useReactTable,
} from "@tanstack/react-table"
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/components/ui/avatar"
interface IData {
id: string
name: string
availability: "online" | "away" | "busy" | "offline"
avatar: string
status: "active" | "inactive"
flag: string // Emoji flags
email: string
company: string
role: string
joined: string
location: string
balance: number
}
const demoData: IData[] = [
{
id: "1",
name: "Alex Johnson",
availability: "online",
avatar:
"https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "us",
email: "alex@apple.com",
company: "Apple",
role: "CEO",
joined: "Jan, 2024",
location: "United States",
balance: 5143.03,
},
{
id: "2",
name: "Sarah Chen",
availability: "away",
avatar:
"https://images.unsplash.com/photo-1519699047748-de8e457a634e?w=96&h=96&dpr=2&q=80",
status: "inactive",
flag: "gb",
email: "sarah@openai.com",
company: "OpenAI",
role: "CTO",
joined: "Mar, 2023",
location: "United Kingdom",
balance: 4321.87,
},
{
id: "3",
name: "Michael Rodriguez",
availability: "busy",
avatar:
"https://images.unsplash.com/photo-1584308972272-9e4e7685e80f?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "ca",
email: "michael@meta.com",
company: "Meta",
role: "Designer",
joined: "Jun, 2022",
location: "Canada",
balance: 7654.98,
},
{
id: "4",
name: "Emma Wilson",
availability: "offline",
avatar:
"https://images.unsplash.com/photo-1485893086445-ed75865251e0?w=96&h=96&dpr=2&q=80",
status: "inactive",
flag: "au",
email: "emma@tesla.com",
company: "Tesla",
role: "Developer",
joined: "Sep, 2024",
location: "Australia",
balance: 3456.45,
},
{
id: "5",
name: "David Kim",
availability: "online",
avatar:
"https://images.unsplash.com/photo-1607990281513-2c110a25bd8c?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "de",
email: "david@sap.com",
company: "SAP",
role: "Lawyer",
joined: "Nov, 2023",
location: "Germany",
balance: 9876.54,
},
{
id: "6",
name: "Aron Thompson",
availability: "away",
avatar:
"https://images.unsplash.com/photo-1527980965255-d3b416303d12?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "my",
email: "aron@keenthemes.com",
company: "Keenthemes",
role: "Director",
joined: "Feb, 2022",
location: "Malaysia",
balance: 6214.22,
},
{
id: "7",
name: "James Brown",
availability: "busy",
avatar:
"https://images.unsplash.com/photo-1543299750-19d1d6297053?w=96&h=96&dpr=2&q=80",
status: "inactive",
flag: "es",
email: "james@bbva.es",
company: "BBVA",
role: "Product Manager",
joined: "Aug, 2024",
location: "Spain",
balance: 5321.77,
},
{
id: "8",
name: "Maria Garcia",
availability: "offline",
avatar:
"https://images.unsplash.com/photo-1620075225255-8c2051b6c015?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "jp",
email: "maria@sony.jp",
company: "Sony",
role: "Marketing Lead",
joined: "Dec, 2023",
location: "Japan",
balance: 8452.39,
},
{
id: "9",
name: "Nick Johnson",
availability: "online",
avatar:
"https://images.unsplash.com/photo-1485206412256-701ccc5b93ca?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "fr",
email: "nick@lvmh.fr",
company: "LVMH",
role: "Data Scientist",
joined: "Apr, 2022",
location: "France",
balance: 7345.1,
},
{
id: "10",
name: "Liam Thompson",
availability: "away",
avatar:
"https://images.unsplash.com/photo-1542595913-85d69b0edbaf?w=96&h=96&dpr=2&q=80",
status: "inactive",
flag: "it",
email: "liam@eni.it",
company: "ENI",
role: "Engineer",
joined: "Jul, 2024",
location: "Italy",
balance: 5214.88,
},
{
id: "11",
name: "Alex Johnson",
availability: "busy",
avatar:
"https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "br",
email: "alex@vale.br",
company: "Vale",
role: "Software Engineer",
joined: "May, 2023",
location: "Brazil",
balance: 9421.5,
},
{
id: "12",
name: "Sarah Chen",
availability: "offline",
avatar:
"https://images.unsplash.com/photo-1519699047748-de8e457a634e?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "in",
email: "sarah@tata.in",
company: "Tata",
role: "Sales Manager",
joined: "Oct, 2024",
location: "India",
balance: 4521.67,
},
]
export function Pattern() {
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 5,
})
const [sorting, setSorting] = useState<SortingState>([
{ id: "name", desc: true },
])
const columns = useMemo<ColumnDef<IData>[]>(
() => [
{
accessorKey: "name",
id: "name",
header: "Name",
cell: ({ row }) => {
return (
<div className="flex items-center gap-2">
<Avatar className="size-6">
<AvatarImage
src={row.original.avatar}
alt={row.original.name}
/>
<AvatarFallback>
{row.original.name
.split(" ")
.map((n) => n[0])
.join("")}
</AvatarFallback>
</Avatar>
<a
href="#"
className="text-foreground hover:text-primary font-medium"
>
{row.original.name}
</a>
</div>
)
},
size: 175,
enableSorting: true,
enableHiding: false,
},
{
accessorKey: "email",
header: "Email",
cell: (info) => (
<a
href={`mailto:${info.getValue()}`}
className="hover:text-primary hover:underline"
>
{info.getValue() as string}
</a>
),
size: 180,
},
{
accessorKey: "location",
header: "Location",
cell: ({ row }) => {
return (
<div className="flex items-center gap-1.5">
<img
src={`https://flagcdn.com/${row.original.flag.toLowerCase()}.svg`}
alt={row.original.flag}
className="size-4 rounded-full object-cover"
/>
<div className="text-foreground font-medium">
{row.original.location}
</div>
</div>
)
},
size: 170,
},
{
accessorKey: "balance",
header: "Balance ($)",
cell: (info) => (
<span className="font-semibold">
${(info.getValue() as number).toFixed(2)}
</span>
),
size: 120,
meta: {
headerClassName: "text-right rtl:text-left",
cellClassName: "text-right rtl:text-left",
},
},
],
[]
)
const table = useReactTable({
columns,
data: demoData,
pageCount: Math.ceil((demoData?.length || 0) / pagination.pageSize),
getRowId: (row: IData) => row.id,
state: {
pagination,
sorting,
},
onPaginationChange: setPagination,
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
})
return (
<DataGrid
table={table}
recordCount={demoData?.length || 0}
tableLayout={{
stripped: true,
rowRounded: true,
}}
>
<div className="w-full space-y-2.5">
<DataGridContainer border={false}>
<DataGridScrollArea>
<DataGridTable />
</DataGridScrollArea>
</DataGridContainer>
<DataGridPagination />
</div>
</DataGrid>
)
}
"use client"
import { useMemo, useState } from "react"
import {
DataGrid,
DataGridContainer,
} from "@/components/reui/data-grid/data-grid"
import { DataGridPagination } from "@/components/reui/data-grid/data-grid-pagination"
import { DataGridScrollArea } from "@/components/reui/data-grid/data-grid-scroll-area"
import { DataGridTable } from "@/components/reui/data-grid/data-grid-table"
import {
ColumnDef,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
PaginationState,
SortingState,
useReactTable,
} from "@tanstack/react-table"
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/components/ui/avatar"
interface Data {
id: string
name: string
availability: "online" | "away" | "busy" | "offline"
avatar: string
status: "active" | "inactive"
flag: string // Emoji flags
email: string
company: string
role: string
joined: string
location: string
balance: number
}
const demoData: Data[] = [
{
id: "1",
name: "Alex Johnson",
availability: "online",
avatar:
"https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "us",
email: "alex@apple.com",
company: "Apple",
role: "CEO",
joined: "Jan, 2026",
location: "United States",
balance: 5143.03,
},
{
id: "2",
name: "Sarah Chen",
availability: "away",
avatar:
"https://images.unsplash.com/photo-1519699047748-de8e457a634e?w=96&h=96&dpr=2&q=80",
status: "inactive",
flag: "gb",
email: "sarah@openai.com",
company: "OpenAI",
role: "CTO",
joined: "Jul, 2025",
location: "United Kingdom",
balance: 4321.87,
},
{
id: "3",
name: "Michael Rodriguez",
availability: "busy",
avatar:
"https://images.unsplash.com/photo-1584308972272-9e4e7685e80f?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "ca",
email: "michael@meta.com",
company: "Meta",
role: "Designer",
joined: "Mar, 2019",
location: "Canada",
balance: 7654.98,
},
{
id: "4",
name: "Emma Wilson",
availability: "offline",
avatar:
"https://images.unsplash.com/photo-1485893086445-ed75865251e0?w=96&h=96&dpr=2&q=80",
status: "inactive",
flag: "au",
email: "emma@tesla.com",
company: "Tesla",
role: "Developer",
joined: "Jan, 2024",
location: "Australia",
balance: 3456.45,
},
{
id: "5",
name: "David Kim",
availability: "online",
avatar:
"https://images.unsplash.com/photo-1607990281513-2c110a25bd8c?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "de",
email: "david@sap.com",
company: "SAP",
role: "Lawyer",
joined: "May, 2023",
location: "Germany",
balance: 9876.54,
},
{
id: "6",
name: "Aron Thompson",
availability: "away",
avatar:
"https://images.unsplash.com/photo-1527980965255-d3b416303d12?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "my",
email: "aron@keenthemes.com",
company: "Keenthemes",
role: "Director",
joined: "Nov, 2018",
location: "Malaysia",
balance: 6214.22,
},
{
id: "7",
name: "James Brown",
availability: "busy",
avatar:
"https://images.unsplash.com/photo-1543299750-19d1d6297053?w=96&h=96&dpr=2&q=80",
status: "inactive",
flag: "es",
email: "james@bbva.es",
company: "BBVA",
role: "Product Manager",
joined: "Jun, 2021",
location: "Spain",
balance: 5321.77,
},
{
id: "8",
name: "Maria Garcia",
availability: "offline",
avatar:
"https://images.unsplash.com/photo-1620075225255-8c2051b6c015?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "jp",
email: "maria@sony.jp",
company: "Sony",
role: "Marketing Lead",
joined: "Oct, 2020",
location: "Japan",
balance: 8452.39,
},
{
id: "9",
name: "Nick Johnson",
availability: "online",
avatar:
"https://images.unsplash.com/photo-1485206412256-701ccc5b93ca?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "fr",
email: "nick@lvmh.fr",
company: "LVMH",
role: "Data Scientist",
joined: "Sep, 2019",
location: "France",
balance: 7345.1,
},
{
id: "10",
name: "Liam Thompson",
availability: "away",
avatar:
"https://images.unsplash.com/photo-1542595913-85d69b0edbaf?w=96&h=96&dpr=2&q=80",
status: "inactive",
flag: "it",
email: "liam@eni.it",
company: "ENI",
role: "Engineer",
joined: "Feb, 2023",
location: "Italy",
balance: 5214.88,
},
{
id: "11",
name: "Alex Johnson",
availability: "busy",
avatar:
"https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "br",
email: "alex@vale.br",
company: "Vale",
role: "Software Engineer",
joined: "Dec, 2022",
location: "Brazil",
balance: 9421.5,
},
{
id: "12",
name: "Sarah Chen",
availability: "offline",
avatar:
"https://images.unsplash.com/photo-1519699047748-de8e457a634e?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "in",
email: "sarah@tata.in",
company: "Tata",
role: "Sales Manager",
joined: "Mar, 2020",
location: "India",
balance: 4521.67,
},
]
export function Pattern() {
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 5,
})
const [sorting, setSorting] = useState<SortingState>([
{ id: "name", desc: true },
])
const columns = useMemo<ColumnDef<Data>[]>(
() => [
{
accessorKey: "name",
id: "name",
header: "Name",
cell: ({ row }) => {
return (
<div className="flex items-center gap-2">
<Avatar className="size-6">
<AvatarImage
src={row.original.avatar}
alt={row.original.name}
/>
<AvatarFallback>
{row.original.name
.split(" ")
.map((n) => n[0])
.join("")}
</AvatarFallback>
</Avatar>
<a
href="#"
className="text-foreground hover:text-primary font-medium"
>
{row.original.name}
</a>
</div>
)
},
size: 225,
enableSorting: true,
enableHiding: false,
},
{
accessorKey: "email",
header: "Email",
cell: (info) => (
<a
href={`mailto:${info.getValue()}`}
className="hover:text-primary hover:underline"
>
{info.getValue() as string}
</a>
),
size: 200,
},
{
accessorKey: "location",
header: "Location",
cell: ({ row }) => (
<div className="flex items-center gap-1.5">
<img
src={`https://flagcdn.com/${row.original.flag.toLowerCase()}.svg`}
alt={row.original.flag}
className="size-4 rounded-full object-cover"
/>
<div className="text-foreground font-medium">
{row.original.location}
</div>
</div>
),
size: 175,
},
{
accessorKey: "joined",
header: "Joined",
cell: (info) => info.getValue() as string,
size: 120,
meta: {
cellClassName: "font-medium",
},
},
],
[]
)
const table = useReactTable({
columns,
data: demoData,
pageCount: Math.ceil((demoData?.length || 0) / pagination.pageSize),
getRowId: (row: Data) => row.id,
state: {
pagination,
sorting,
},
columnResizeMode: "onChange",
onPaginationChange: setPagination,
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
})
return (
<DataGrid
table={table}
recordCount={demoData?.length || 0}
tableLayout={{
width: "auto",
}}
>
<div className="w-full space-y-2.5">
<DataGridContainer>
<DataGridScrollArea>
<DataGridTable />
</DataGridScrollArea>
</DataGridContainer>
<DataGridPagination />
</div>
</DataGrid>
)
}
"use client"
import { useEffect, useMemo, useState } from "react"
import {
DataGrid,
DataGridContainer,
} from "@/components/reui/data-grid/data-grid"
import { DataGridPagination } from "@/components/reui/data-grid/data-grid-pagination"
import { DataGridScrollArea } from "@/components/reui/data-grid/data-grid-scroll-area"
import {
DataGridTable,
DataGridTableRowSelect,
DataGridTableRowSelectAll,
} from "@/components/reui/data-grid/data-grid-table"
import {
ColumnDef,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
PaginationState,
RowSelectionState,
SortingState,
useReactTable,
} from "@tanstack/react-table"
import { cn } from "@/lib/utils"
import {
Avatar,
AvatarBadge,
AvatarFallback,
AvatarImage,
} from "@/components/ui/avatar"
interface IData {
id: string
name: string
availability: "online" | "away" | "busy" | "offline"
avatar: string
status: "active" | "inactive"
flag: string
email: string
company: string
role: string
joined: string
location: string
balance: number
}
const demoData: IData[] = [
{
id: "1",
name: "Alex Johnson",
availability: "online",
avatar:
"https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "us",
email: "alex@apple.com",
company: "Apple",
role: "CEO",
joined: "Apr, 2021",
location: "United States",
balance: 5143.03,
},
{
id: "2",
name: "Sarah Chen",
availability: "away",
avatar:
"https://images.unsplash.com/photo-1519699047748-de8e457a634e?w=96&h=96&dpr=2&q=80",
status: "inactive",
flag: "gb",
email: "sarah@openai.com",
company: "OpenAI",
role: "CTO",
joined: "Jul, 2020",
location: "United Kingdom",
balance: 4321.87,
},
{
id: "3",
name: "Michael Rodriguez",
availability: "busy",
avatar:
"https://images.unsplash.com/photo-1584308972272-9e4e7685e80f?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "ca",
email: "michael@meta.com",
company: "Meta",
role: "Designer",
joined: "Mar, 2019",
location: "Canada",
balance: 7654.98,
},
{
id: "4",
name: "Emma Wilson",
availability: "offline",
avatar:
"https://images.unsplash.com/photo-1485893086445-ed75865251e0?w=96&h=96&dpr=2&q=80",
status: "inactive",
flag: "au",
email: "emma@tesla.com",
company: "Tesla",
role: "Developer",
joined: "Jan, 2022",
location: "Australia",
balance: 3456.45,
},
{
id: "5",
name: "David Kim",
availability: "online",
avatar:
"https://images.unsplash.com/photo-1607990281513-2c110a25bd8c?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "de",
email: "david@sap.com",
company: "SAP",
role: "Lawyer",
joined: "May, 2023",
location: "Germany",
balance: 9876.54,
},
{
id: "6",
name: "Aron Thompson",
availability: "away",
avatar:
"https://images.unsplash.com/photo-1527980965255-d3b416303d12?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "my",
email: "aron@keenthemes.com",
company: "Keenthemes",
role: "Director",
joined: "Nov, 2018",
location: "Malaysia",
balance: 6214.22,
},
{
id: "7",
name: "James Brown",
availability: "busy",
avatar:
"https://images.unsplash.com/photo-1543299750-19d1d6297053?w=96&h=96&dpr=2&q=80",
status: "inactive",
flag: "es",
email: "james@bbva.es",
company: "BBVA",
role: "Product Manager",
joined: "Jun, 2021",
location: "Spain",
balance: 5321.77,
},
{
id: "8",
name: "Maria Garcia",
availability: "offline",
avatar:
"https://images.unsplash.com/photo-1620075225255-8c2051b6c015?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "jp",
email: "maria@sony.jp",
company: "Sony",
role: "Marketing Lead",
joined: "Oct, 2020",
location: "Japan",
balance: 8452.39,
},
{
id: "9",
name: "Nick Johnson",
availability: "online",
avatar:
"https://images.unsplash.com/photo-1485206412256-701ccc5b93ca?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "fr",
email: "nick@lvmh.fr",
company: "LVMH",
role: "Data Scientist",
joined: "Sep, 2019",
location: "France",
balance: 7345.1,
},
{
id: "10",
name: "Liam Thompson",
availability: "away",
avatar:
"https://images.unsplash.com/photo-1542595913-85d69b0edbaf?w=96&h=96&dpr=2&q=80",
status: "inactive",
flag: "it",
email: "liam@eni.it",
company: "ENI",
role: "Engineer",
joined: "Feb, 2023",
location: "Italy",
balance: 5214.88,
},
{
id: "11",
name: "Alex Johnson",
availability: "busy",
avatar:
"https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "br",
email: "alex@vale.br",
company: "Vale",
role: "Software Engineer",
joined: "Dec, 2022",
location: "Brazil",
balance: 9421.5,
},
{
id: "12",
name: "Sarah Chen",
availability: "offline",
avatar:
"https://images.unsplash.com/photo-1519699047748-de8e457a634e?w=96&h=96&dpr=2&q=80",
status: "active",
flag: "in",
email: "sarah@tata.in",
company: "Tata",
role: "Sales Manager",
joined: "Mar, 2020",
location: "India",
balance: 4521.67,
},
]
export function Pattern() {
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 5,
})
const [sorting, setSorting] = useState<SortingState>([
{ id: "name", desc: true },
])
const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [selectedIds, setSelectedIds] = useState<string[]>([])
useEffect(() => {
const selectedRowIds = Object.keys(rowSelection)
if (selectedRowIds.length > 0) {
setSelectedIds(selectedRowIds)
} else {
setSelectedIds([])
}
}, [rowSelection])
const columns = useMemo<ColumnDef<IData>[]>(
() => [
{
accessorKey: "id",
header: () => <DataGridTableRowSelectAll />,
cell: ({ row }) => <DataGridTableRowSelect row={row} />,
enableSorting: false,
size: 20,
meta: {
headerClassName: "",
cellClassName: "",
},
},
{
accessorKey: "name",
id: "name",
header: "Name",
cell: ({ row }) => {
const availability = row.original.availability
const statusColors = {
online: "bg-green-500",
away: "bg-yellow-500",
busy: "bg-orange-500",
offline: "bg-gray-400",
}
return (
<div className="flex items-center gap-3">
<Avatar className="size-8">
<AvatarImage
src={row.original.avatar}
alt={row.original.name}
/>
<AvatarFallback>
{row.original.name
.split(" ")
.map((n) => n[0])
.join("")}
</AvatarFallback>
<AvatarBadge
className={cn(
"size-1.5! p-0",
statusColors[availability] || statusColors.offline
)}
/>
</Avatar>
<div className="space-y-px">
<div className="text-foreground font-medium">
{row.original.name}
</div>
<div className="text-muted-foreground">
{row.original.email}
</div>
</div>
</div>
)
},
size: 200,
enableSorting: true,
enableHiding: false,
},
{
accessorKey: "location",
header: "Location",
cell: ({ row }) => {
return (
<div className="flex items-center gap-1.5">
<img
src={`https://flagcdn.com/${row.original.flag.toLowerCase()}.svg`}
alt={row.original.flag}
className="size-4 rounded-full object-cover"
/>
<div className="text-foreground font-medium">
{row.original.location}
</div>
</div>
)
},
size: 180,
meta: {
headerClassName: "",
cellClassName: "text-start",
},
},
{
accessorKey: "joined",
header: "Joined",
cell: (info) => info.getValue() as string,
size: 120,
meta: {
headerClassName: "",
cellClassName: "font-medium",
},
},
],
[]
)
const table = useReactTable({
columns,
data: demoData,
pageCount: Math.ceil((demoData?.length || 0) / pagination.pageSize),
getRowId: (row: IData) => row.id,
state: {
pagination,
sorting,
rowSelection,
},
enableRowSelection: true,
onRowSelectionChange: setRowSelection,
onPaginationChange: setPagination,
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
})
return (
<DataGrid table={table} recordCount={demoData?.length || 0}>
<div className="w-full space-y-2.5">
<DataGridContainer>
<DataGridScrollArea>
<DataGridTable />
</DataGridScrollArea>
</DataGridContainer>
<DataGridPagination />
</div>
</DataGrid>
)
}