table-datagrid-builder
This skill builds data tables/grids with sorting, filtering, pagination, and row actions. Supports React (TanStack Table, AG Grid), Vue 3 (Vue Good Table, AG Grid), with server/client-side data. Creates tables with column configuration, sorting (multi-column), filtering (text, date, number), pagination, row selection, export (CSV/Excel). Activate when user says "create table", "data grid", "display list", "show users table", or needs tabular data display.
- Category: discovered
- Tags: github, community
- Version: 1.0.0
- Author: walid
- Downloads: 0
- Updated: 2025-11-22
Download the latest version (v1.0.0) from this page.
SKILL.md
---
name: table-datagrid-builder
description: |
This skill builds data tables/grids with sorting, filtering, pagination, and row actions.
Supports React (TanStack Table, AG Grid), Vue 3 (Vue Good Table, AG Grid), with server/client-side data.
Creates tables with column configuration, sorting (multi-column), filtering (text, date, number), pagination, row selection, export (CSV/Excel).
Activate when user says "create table", "data grid", "display list", "show users table", or needs tabular data display.
Output: Production-ready tables with sorting, filtering, pagination, accessibility, and export functionality.
---
# Table/DataGrid Builder
> **Purpose**: Build data tables with sorting, filtering, pagination, and actions
---
## When to Use
- ✅ Display lists of data (users, products, orders)
- ✅ Need sorting, filtering, pagination
- ✅ Row actions (edit, delete, view)
- ✅ Export to CSV/Excel
- ✅ User says: "create table", "show list", "data grid"
---
## React + TanStack Table
```tsx
import {
useReactTable,
getCoreRowModel,
getSortedRowModel,
getFilteredRowModel,
getPaginationRowModel,
flexRender,
ColumnDef,
SortingState,
ColumnFiltersState
} from '@tanstack/react-table';
import { useState } from 'react';
interface User {
id: number;
name: string;
email: string;
role: string;
status: 'active' | 'inactive';
createdAt: string;
}
const columns: ColumnDef<User>[] = [
{
accessorKey: 'name',
header: 'Name',
cell: info => info.getValue()
},
{
accessorKey: 'email',
header: 'Email'
},
{
accessorKey: 'role',
header: 'Role',
filterFn: 'equals'
},
{
accessorKey: 'status',
header: 'Status',
cell: ({ getValue }) => {
const status = getValue() as string;
return (
<span className={`px-2 py-1 rounded ${
status === 'active' ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'
}`}>
{status}
</span>
);
}
},
{
accessorKey: 'createdAt',
header: 'Created',
cell: info => new Date(info.getValue() as string).toLocaleDateString()
},
{
id: 'actions',
header: 'Actions',
cell: ({ row }) => (
<div className="flex gap-2">
<button onClick={() => handleEdit(row.original)} className="text-blue-600">Edit</button>
<button onClick={() => handleDelete(row.original.id)} className="text-red-600">Delete</button>
</div>
)
}
];
export function UsersTable({ data }: { data: User[] }) {
const [sorting, setSorting] = useState<SortingState>([]);
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
const [globalFilter, setGlobalFilter] = useState('');
const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 });
const table = useReactTable({
data,
columns,
state: { sorting, columnFilters, globalFilter, pagination },
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
onGlobalFilterChange: setGlobalFilter,
onPaginationChange: setPagination,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel()
});
return (
<div>
{/* Global Search */}
<div className="mb-4">
<input
value={globalFilter ?? ''}
onChange={e => setGlobalFilter(e.target.value)}
placeholder="Search all columns..."
className="px-3 py-2 border rounded w-full max-w-sm"
/>
</div>
{/* Table */}
<div className="overflow-x-auto border rounded">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
{table.getHeaderGroups().map(headerGroup => (
<tr key={headerGroup.id}>
{headerGroup.headers.map(header => (
<th
key={header.id}
onClick={header.column.getToggleSortingHandler()}
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100"
>
<div className="flex items-center gap-2">
{flexRender(header.column.columnDef.header, header.getContext())}
{header.column.getIsSorted() && (
<span>{header.column.getIsSorted() === 'asc' ? '↑' : '↓'}</span>
)}
</div>
</th>
))}
</tr>
))}
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{table.getRowModel().rows.map(row => (
<tr key={row.id} className="hover:bg-gray-50">
{row.getVisibleCells().map(cell => (
<td key={cell.id} className="px-6 py-4 whitespace-nowrap text-sm">
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
{/* Pagination */}
<div className="mt-4 flex items-center justify-between">
<div className="flex gap-2">
<button
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
className="px-3 py-1 border rounded disabled:opacity-50"
>
Previous
</button>
<button
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
className="px-3 py-1 border rounded disabled:opacity-50"
>
Next
</button>
</div>
<span className="text-sm text-gray-700">
Page {table.getState().pagination.pageIndex + 1} of {table.getPageCount()}
</span>
<select
value={table.getState().pagination.pageSize}
onChange={e => table.setPageSize(Number(e.target.value))}
className="px-2 py-1 border rounded"
>
{[10, 20, 50, 100].map(size => (
<option key={size} value={size}>Show {size}</option>
))}
</select>
</div>
</div>
);
}
```
---
## Export to CSV
```typescript
function exportToCSV(data: any[], filename: string) {
// Convert to CSV
const headers = Object.keys(data[0]);
const csvContent = [
headers.join(','),
...data.map(row => headers.map(header => row[header]).join(','))
].join('\n');
// Download
const blob = new Blob([csvContent], { type: 'text/csv' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `${filename}.csv`;
link.click();
}
// Usage
<button onClick={() => exportToCSV(data, 'users')}>
Export to CSV
</button>
```
---
**Skill Version**: 1.0.0
**Technologies**: React (TanStack Table), Vue 3 (AG Grid, Vue Good Table)
**Output**: Tables with sorting, filtering, pagination, row actions, export