UNPKG

nextjs-reusable-table

Version:

A production-ready, highly customizable and reusable table component for Next.js applications. Features include sorting, pagination, search, dark mode, TypeScript support, and zero dependencies.

1,057 lines (936 loc) 31 kB
# 📋 Comprehensive Examples This document provides extensive examples for all features and use cases of the nextjs-reusable-table component. ## Table of Contents - [Basic Examples](#basic-examples) - [Data Formatting](#data-formatting) - [Interactive Features](#interactive-features) - [Styling and Theming](#styling-and-theming) - [Advanced Use Cases](#advanced-use-cases) - [Real-World Scenarios](#real-world-scenarios) ## Basic Examples ### Simple Data Table ```tsx "use client"; import React from "react"; import { TableComponent } from "nextjs-reusable-table"; import "nextjs-reusable-table/dist/index.css"; interface Product { id: number; name: string; price: number; category: string; } const products: Product[] = [ { id: 1, name: "Laptop", price: 999, category: "Electronics" }, { id: 2, name: "Chair", price: 299, category: "Furniture" }, { id: 3, name: "Book", price: 19, category: "Education" }, ]; export default function SimpleProductTable() { return ( <TableComponent<Product> columns={["ID", "Product Name", "Price", "Category"]} data={products} props={["id", "name", "price", "category"]} /> ); } ``` ### Empty State Handling ```tsx export default function EmptyStateTable() { const emptyData: Product[] = []; return ( <TableComponent<Product> columns={["ID", "Product Name", "Price", "Category"]} data={emptyData} props={["id", "name", "price", "category"]} noContentProps={{ text: "No products found. Add some products to get started!", icon: <span className="text-6xl">📦</span>, }} /> ); } ``` ### Loading State ```tsx export default function LoadingTable() { const [isLoading, setIsLoading] = useState(true); const [data, setData] = useState<Product[]>([]); useEffect(() => { // Simulate API call setTimeout(() => { setData(products); setIsLoading(false); }, 2000); }, []); return ( <TableComponent<Product> columns={["ID", "Product Name", "Price", "Category"]} data={data} props={["id", "name", "price", "category"]} loading={isLoading} /> ); } ``` ## Data Formatting ### Automatic Data Type Handling ```tsx interface Employee { id: number; name: string; email: string; joinDate: string; // ISO date string skills: string[]; // Array website: string; // URL salary: number; active: boolean; } const employees: Employee[] = [ { id: 1, name: "Sarah Chen", email: "sarah.chen@company.com", joinDate: "2023-03-15T08:30:00Z", skills: ["React", "TypeScript", "Node.js", "GraphQL", "AWS"], website: "https://sarahchen.dev", salary: 85000, active: true, }, { id: 2, name: "Mike Johnson", email: "mike.j@company.com", joinDate: "2022-11-20T09:00:00Z", skills: ["Python", "Django", "PostgreSQL", "Docker", "Kubernetes", "Machine Learning"], website: "https://mikejohnson.io", salary: 92000, active: false, }, ]; export default function AutoFormattedTable() { return ( <TableComponent<Employee> columns={["ID", "Name", "Email", "Join Date", "Skills", "Website", "Salary", "Status"]} data={employees} props={["id", "name", "email", "joinDate", "skills", "website", "salary", "active"]} /> ); } // This will automatically: // - Format joinDate as readable date // - Display skills as chips with "show more" for long arrays // - Render website as clickable link // - Show active status as boolean value ``` ### Custom Data Formatting ```tsx export default function CustomFormattedTable() { const formatValue = (value: string, prop: string, item: Employee) => { switch (prop) { case "salary": return new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", }).format(Number(value)); case "active": return ( <span className={`px-2 py-1 rounded-full text-xs font-medium ${ item.active ? "bg-green-100 text-green-800" : "bg-red-100 text-red-800" }`}> {item.active ? "Active" : "Inactive"} </span> ); case "name": return ( <div className="flex items-center space-x-2"> <div className="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center text-white font-medium"> {item.name.charAt(0)} </div> <span className="font-medium">{value}</span> </div> ); default: return value; } }; return ( <TableComponent<Employee> columns={["ID", "Name", "Email", "Join Date", "Skills", "Website", "Salary", "Status"]} data={employees} props={["id", "name", "email", "joinDate", "skills", "website", "salary", "active"]} formatValue={formatValue} /> ); } ``` ### Custom Header Formatting ```tsx export default function CustomHeaderTable() { const formatHeader = (header: string, prop: string, index: number) => { const icons: Record<string, string> = { id: "🆔", name: "👤", email: "📧", salary: "💰", active: "⚡", }; return ( <div className="flex items-center space-x-2"> <span>{icons[prop] || "📋"}</span> <span className="font-bold uppercase tracking-wider text-xs"> {header} </span> </div> ); }; return ( <TableComponent<Employee> columns={["ID", "Name", "Email", "Salary", "Status"]} data={employees} props={["id", "name", "email", "salary", "active"]} formatHeader={formatHeader} /> ); } ``` ## Interactive Features ### Row Actions ```tsx export default function ActionTable() { const editEmployee = (employee: Employee) => { console.log("Edit employee:", employee); // Open edit modal, navigate to edit page, etc. }; const deleteEmployee = (employee: Employee) => { if (window.confirm(\`Delete \${employee.name}?\`)) { console.log("Delete employee:", employee); // Call delete API, update state, etc. } }; const viewDetails = (employee: Employee) => { console.log("View employee details:", employee); // Navigate to details page, open modal, etc. }; return ( <TableComponent<Employee> columns={["Name", "Email", "Join Date", "Status"]} data={employees} props={["name", "email", "joinDate", "active"]} actions={true} actionTexts={["View", "Edit", "Delete"]} actionFunctions={[viewDetails, editEmployee, deleteEmployee]} /> ); } ``` ### Row Click Handling ```tsx export default function ClickableRowsTable() { const handleRowClick = (employee: Employee) => { console.log("Row clicked:", employee); // Navigate to detail page router.push(\`/employees/\${employee.id}\`); }; return ( <TableComponent<Employee> columns={["Name", "Email", "Join Date", "Status"]} data={employees} props={["name", "email", "joinDate", "active"]} rowOnClick={handleRowClick} // Add visual indicator for clickable rows customClassNames={{ tr: "cursor-pointer hover:bg-gray-50 transition-colors", }} /> ); } ``` ### Sorting ```tsx export default function SortableTable() { const [sortConfig, setSortConfig] = useState<{ prop: keyof Employee; order: "asc" | "desc"; } | null>(null); const handleSort = (prop: keyof Employee) => { let order: "asc" | "desc" = "asc"; if (sortConfig && sortConfig.prop === prop) { order = sortConfig.order === "asc" ? "desc" : "asc"; } setSortConfig({ prop, order }); }; const sortedEmployees = useMemo(() => { if (!sortConfig) return employees; return [...employees].sort((a, b) => { const aValue = a[sortConfig.prop]; const bValue = b[sortConfig.prop]; if (aValue < bValue) return sortConfig.order === "asc" ? -1 : 1; if (aValue > bValue) return sortConfig.order === "asc" ? 1 : -1; return 0; }); }, [employees, sortConfig]); return ( <div> <div className="mb-4 text-sm text-gray-600"> {sortConfig && ( <span> Sorted by {sortConfig.prop} ({sortConfig.order}) </span> )} </div> <TableComponent<Employee> columns={["Name", "Email", "Join Date", "Salary"]} data={sortedEmployees} props={["name", "email", "joinDate", "salary"]} sortableProps={["name", "email", "joinDate", "salary"]} onSort={handleSort} /> </div> ); } ``` ### Search and Filtering ```tsx export default function SearchFilterTable() { const [searchTerm, setSearchTerm] = useState(""); const [statusFilter, setStatusFilter] = useState<"all" | "active" | "inactive">("all"); const filteredEmployees = useMemo(() => { return employees.filter((employee) => { // Apply search filter const matchesSearch = [ employee.name, employee.email, employee.skills.join(" "), ] .join(" ") .toLowerCase() .includes(searchTerm.toLowerCase()); // Apply status filter const matchesStatus = statusFilter === "all" || (statusFilter === "active" && employee.active) || (statusFilter === "inactive" && !employee.active); return matchesSearch && matchesStatus; }); }, [searchTerm, statusFilter]); return ( <div className="space-y-4"> <div className="flex flex-col sm:flex-row gap-4"> <div className="flex-1"> <input type="text" placeholder="Search by name, email, or skills..." value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" /> </div> <div className="sm:w-48"> <select value={statusFilter} onChange={(e) => setStatusFilter(e.target.value as "all" | "active" | "inactive")} className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" > <option value="all">All Employees</option> <option value="active">Active Only</option> <option value="inactive">Inactive Only</option> </select> </div> </div> <div className="text-sm text-gray-600"> Showing {filteredEmployees.length} of {employees.length} employees </div> <TableComponent<Employee> columns={["Name", "Email", "Join Date", "Skills", "Status"]} data={filteredEmployees} props={["name", "email", "joinDate", "skills", "active"]} searchValue={searchTerm} // This enables built-in search highlighting /> </div> ); } ``` ### Pagination ```tsx export default function PaginatedTable() { const [currentPage, setCurrentPage] = useState(1); const [itemsPerPage, setItemsPerPage] = useState(10); return ( <div className="space-y-4"> <div className="flex justify-between items-center"> <div className="text-sm text-gray-600"> Total: {employees.length} employees </div> <div className="flex items-center space-x-2"> <label htmlFor="per-page" className="text-sm text-gray-600"> Items per page: </label> <select id="per-page" value={itemsPerPage} onChange={(e) => { setItemsPerPage(Number(e.target.value)); setCurrentPage(1); // Reset to first page }} className="p-1 border border-gray-300 rounded" > <option value={5}>5</option> <option value={10}>10</option> <option value={25}>25</option> <option value={50}>50</option> </select> </div> </div> <TableComponent<Employee> columns={["Name", "Email", "Join Date", "Status"]} data={employees} props={["name", "email", "joinDate", "active"]} enablePagination page={currentPage} setPage={setCurrentPage} itemsPerPage={itemsPerPage} /> </div> ); } ``` ## Styling and Theming ### Dark Mode ```tsx export default function DarkModeTable() { return ( <div className="p-6 bg-gray-900 min-h-screen"> <h1 className="text-2xl font-bold text-white mb-6">Dark Mode Table</h1> <TableComponent<Employee> columns={["Name", "Email", "Join Date", "Status"]} data={employees} props={["name", "email", "joinDate", "active"]} enableDarkMode={true} /> </div> ); } ``` ### Custom Styling ```tsx export default function CustomStyledTable() { const customStyles = { container: "rounded-xl shadow-2xl overflow-hidden", table: "w-full bg-gradient-to-r from-purple-50 to-blue-50", thead: "bg-gradient-to-r from-purple-600 to-blue-600 text-white", tbody: "divide-y divide-purple-200", th: "px-6 py-4 text-left text-sm font-bold uppercase tracking-wider", tr: "hover:bg-purple-50 transition-all duration-200 ease-in-out", td: "px-6 py-4 text-sm text-gray-900", pagination: { container: "bg-white px-6 py-4 border-t border-purple-200", button: "bg-purple-600 hover:bg-purple-700 text-white px-4 py-2 rounded-lg font-medium transition-colors", buttonDisabled: "bg-gray-300 text-gray-500 px-4 py-2 rounded-lg font-medium cursor-not-allowed", pageInfo: "text-purple-700 font-medium", }, }; return ( <div className="p-6"> <h1 className="text-3xl font-bold bg-gradient-to-r from-purple-600 to-blue-600 bg-clip-text text-transparent mb-6"> Custom Styled Table </h1> <TableComponent<Employee> columns={["Name", "Email", "Join Date", "Status"]} data={employees} props={["name", "email", "joinDate", "active"]} customClassNames={customStyles} enablePagination page={1} setPage={() => {}} itemsPerPage={5} /> </div> ); } ``` ### Minimal Clean Design ```tsx export default function MinimalTable() { const minimalStyles = { table: "w-full border-collapse", thead: "", tbody: "", th: "border-b-2 border-gray-200 pb-3 text-left text-sm font-semibold text-gray-700 uppercase tracking-wide", tr: "border-b border-gray-100", td: "py-4 text-sm text-gray-800", }; return ( <div className="p-6 bg-white"> <TableComponent<Employee> columns={["Name", "Email", "Status"]} data={employees} props={["name", "email", "active"]} customClassNames={minimalStyles} disableDefaultStyles={false} /> </div> ); } ``` ## Advanced Use Cases ### Master-Detail with Expandable Rows ```tsx interface ExpandableEmployee extends Employee { projects: { name: string; status: string; completion: number }[]; performance: { rating: number; reviews: string[] }; } export default function ExpandableTable() { const [expandedRows, setExpandedRows] = useState<Set<number>>(new Set()); const toggleRow = (employeeId: number) => { const newExpanded = new Set(expandedRows); if (newExpanded.has(employeeId)) { newExpanded.delete(employeeId); } else { newExpanded.add(employeeId); } setExpandedRows(newExpanded); }; const renderRow = (employee: ExpandableEmployee, index: number) => ( <> {/* Main row */} <tr className="border-b hover:bg-gray-50"> <td className="px-6 py-4"> <button onClick={() => toggleRow(employee.id)} className="text-blue-600 hover:text-blue-800 mr-2" > {expandedRows.has(employee.id) ? "−" : "+"} </button> {employee.name} </td> <td className="px-6 py-4">{employee.email}</td> <td className="px-6 py-4">{employee.joinDate}</td> <td className="px-6 py-4"> <span className={\`px-2 py-1 rounded-full text-xs \${ employee.active ? "bg-green-100 text-green-800" : "bg-red-100 text-red-800" }\`}> {employee.active ? "Active" : "Inactive"} </span> </td> </tr> {/* Expanded row content */} {expandedRows.has(employee.id) && ( <tr> <td colSpan={4} className="px-6 py-4 bg-gray-50"> <div className="space-y-4"> <div> <h4 className="font-semibold text-gray-800 mb-2">Current Projects</h4> <div className="grid grid-cols-1 md:grid-cols-2 gap-3"> {employee.projects.map((project, idx) => ( <div key={idx} className="bg-white p-3 rounded border"> <div className="flex justify-between items-center mb-1"> <span className="font-medium">{project.name}</span> <span className={\`px-2 py-1 rounded text-xs \${ project.status === "active" ? "bg-blue-100 text-blue-800" : "bg-gray-100 text-gray-800" }\`}> {project.status} </span> </div> <div className="w-full bg-gray-200 rounded-full h-2"> <div className="bg-blue-600 h-2 rounded-full" style={{ width: \`\${project.completion}%\` }} ></div> </div> <span className="text-xs text-gray-600">{project.completion}% complete</span> </div> ))} </div> </div> <div> <h4 className="font-semibold text-gray-800 mb-2">Performance Rating</h4> <div className="flex items-center space-x-2"> <span className="text-2xl font-bold text-blue-600">{employee.performance.rating}/5</span> <div className="flex"> {[1,2,3,4,5].map(star => ( <span key={star} className={star <= employee.performance.rating ? "text-yellow-400" : "text-gray-300"} > ⭐ </span> ))} </div> </div> </div> </div> </td> </tr> )} </> ); return ( <div className="p-6"> <h2 className="text-2xl font-bold mb-6">Employee Details</h2> <TableComponent<ExpandableEmployee> columns={["Name", "Email", "Join Date", "Status"]} data={expandableEmployeesData} props={["name", "email", "joinDate", "active"]} renderRow={renderRow} /> </div> ); } ``` ### Data Export Functionality ```tsx export default function ExportableTable() { const exportToCSV = () => { const headers = ["Name", "Email", "Join Date", "Status"]; const csvData = [ headers, ...employees.map(emp => [ emp.name, emp.email, new Date(emp.joinDate).toLocaleDateString(), emp.active ? "Active" : "Inactive" ]) ]; const csvContent = csvData.map(row => row.join(",")).join("\\n"); const blob = new Blob([csvContent], { type: "text/csv" }); const url = window.URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = "employees.csv"; a.click(); window.URL.revokeObjectURL(url); }; const exportToJSON = () => { const json = JSON.stringify(employees, null, 2); const blob = new Blob([json], { type: "application/json" }); const url = window.URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = "employees.json"; a.click(); window.URL.revokeObjectURL(url); }; return ( <div className="space-y-4"> <div className="flex justify-between items-center"> <h2 className="text-2xl font-bold">Employee Data</h2> <div className="flex space-x-2"> <button onClick={exportToCSV} className="px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 flex items-center space-x-2" > <span>📊</span> <span>Export CSV</span> </button> <button onClick={exportToJSON} className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 flex items-center space-x-2" > <span>📄</span> <span>Export JSON</span> </button> </div> </div> <TableComponent<Employee> columns={["Name", "Email", "Join Date", "Status"]} data={employees} props={["name", "email", "joinDate", "active"]} /> </div> ); } ``` ### Column Visibility Controls ```tsx export default function ColumnControlTable() { const [visibleColumns, setVisibleColumns] = useState({ id: true, name: true, email: true, joinDate: true, skills: true, salary: false, // Hidden by default active: true, }); const toggleColumn = (column: keyof typeof visibleColumns) => { setVisibleColumns(prev => ({ ...prev, [column]: !prev[column] })); }; const getVisibleColumns = () => { const allColumns = [ { key: "id", label: "ID" }, { key: "name", label: "Name" }, { key: "email", label: "Email" }, { key: "joinDate", label: "Join Date" }, { key: "skills", label: "Skills" }, { key: "salary", label: "Salary" }, { key: "active", label: "Status" }, ]; return allColumns.filter(col => visibleColumns[col.key as keyof typeof visibleColumns]); }; const visibleCols = getVisibleColumns(); return ( <div className="space-y-4"> <div className="bg-gray-50 p-4 rounded-lg"> <h3 className="font-semibold mb-3">Column Visibility</h3> <div className="flex flex-wrap gap-2"> {Object.entries(visibleColumns).map(([key, visible]) => ( <label key={key} className="flex items-center space-x-2 cursor-pointer"> <input type="checkbox" checked={visible} onChange={() => toggleColumn(key as keyof typeof visibleColumns)} className="rounded border-gray-300 text-blue-600 shadow-sm focus:border-blue-300 focus:ring focus:ring-blue-200" /> <span className="text-sm capitalize"> {key === "joinDate" ? "Join Date" : key} </span> </label> ))} </div> </div> <TableComponent<Employee> columns={visibleCols.map(col => col.label)} data={employees} props={visibleCols.map(col => col.key) as (keyof Employee)[]} showRemoveColumns={true} // Enable built-in column hiding /> </div> ); } ``` ## Real-World Scenarios ### E-commerce Product Management ```tsx interface Product { id: string; name: string; sku: string; price: number; stock: number; category: string; status: "active" | "draft" | "archived"; images: string[]; lastModified: string; sales: number; } export default function ProductManagementTable() { const [products, setProducts] = useState<Product[]>([]); const [loading, setLoading] = useState(true); const editProduct = (product: Product) => { // Navigate to edit page or open modal }; const duplicateProduct = (product: Product) => { const newProduct = { ...product, id: \`\${product.id}-copy\`, name: \`\${product.name} (Copy)\`, status: "draft" as const, }; setProducts(prev => [...prev, newProduct]); }; const archiveProduct = (product: Product) => { setProducts(prev => prev.map(p => p.id === product.id ? { ...p, status: "archived" as const } : p ) ); }; const formatValue = (value: string, prop: string, item: Product) => { switch (prop) { case "price": return new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", }).format(Number(value)); case "stock": const stock = Number(value); return ( <span className={stock > 10 ? "text-green-600" : stock > 0 ? "text-yellow-600" : "text-red-600"}> {stock} {stock === 1 ? "unit" : "units"} </span> ); case "status": const statusColors = { active: "bg-green-100 text-green-800", draft: "bg-yellow-100 text-yellow-800", archived: "bg-gray-100 text-gray-800", }; return ( <span className={\`px-2 py-1 rounded-full text-xs font-medium \${statusColors[item.status]}\`}> {item.status.charAt(0).toUpperCase() + item.status.slice(1)} </span> ); case "images": return ( <div className="flex -space-x-2"> {item.images.slice(0, 3).map((img, idx) => ( <img key={idx} src={img} alt="Product" className="w-8 h-8 rounded-full border-2 border-white object-cover" /> ))} {item.images.length > 3 && ( <div className="w-8 h-8 rounded-full border-2 border-white bg-gray-100 flex items-center justify-center text-xs font-medium text-gray-600"> +{item.images.length - 3} </div> )} </div> ); default: return value; } }; return ( <div className="p-6"> <div className="flex justify-between items-center mb-6"> <h1 className="text-3xl font-bold text-gray-900">Product Management</h1> <button className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"> Add Product </button> </div> <TableComponent<Product> columns={["Product", "SKU", "Price", "Stock", "Category", "Status", "Images", "Sales"]} data={products} props={["name", "sku", "price", "stock", "category", "status", "images", "sales"]} loading={loading} formatValue={formatValue} actions={true} actionTexts={["Edit", "Duplicate", "Archive"]} actionFunctions={[editProduct, duplicateProduct, archiveProduct]} enablePagination page={1} setPage={() => {}} itemsPerPage={15} /> </div> ); } ``` ### Customer Support Ticket System ```tsx interface Ticket { id: string; title: string; customer: { name: string; email: string; avatar?: string; }; priority: "low" | "medium" | "high" | "urgent"; status: "open" | "in-progress" | "resolved" | "closed"; assignee?: string; createdAt: string; lastReply: string; tags: string[]; } export default function SupportTicketsTable() { const formatValue = (value: string, prop: string, item: Ticket) => { switch (prop) { case "customer": return ( <div className="flex items-center space-x-3"> <div className="flex-shrink-0"> {item.customer.avatar ? ( <img src={item.customer.avatar} alt={item.customer.name} className="w-8 h-8 rounded-full" /> ) : ( <div className="w-8 h-8 rounded-full bg-blue-500 flex items-center justify-center text-white font-medium text-sm"> {item.customer.name.charAt(0)} </div> )} </div> <div> <div className="font-medium text-gray-900">{item.customer.name}</div> <div className="text-sm text-gray-500">{item.customer.email}</div> </div> </div> ); case "priority": const priorityColors = { low: "bg-gray-100 text-gray-800", medium: "bg-blue-100 text-blue-800", high: "bg-orange-100 text-orange-800", urgent: "bg-red-100 text-red-800", }; return ( <span className={\`px-2 py-1 rounded-full text-xs font-medium \${priorityColors[item.priority]}\`}> {item.priority.charAt(0).toUpperCase() + item.priority.slice(1)} </span> ); case "status": const statusIcons = { open: "🔴", "in-progress": "🟡", resolved: "🟢", closed: "⚫", }; return ( <div className="flex items-center space-x-2"> <span>{statusIcons[item.status]}</span> <span className="capitalize">{item.status.replace("-", " ")}</span> </div> ); case "createdAt": case "lastReply": return ( <div className="text-sm text-gray-600"> {new Date(value).toLocaleDateString()} <div className="text-xs text-gray-400"> {new Date(value).toLocaleTimeString()} </div> </div> ); default: return value; } }; return ( <div className="p-6"> <div className="flex justify-between items-center mb-6"> <h1 className="text-3xl font-bold text-gray-900">Support Tickets</h1> <div className="flex space-x-2"> <select className="border border-gray-300 rounded-lg px-3 py-2"> <option>All Statuses</option> <option>Open</option> <option>In Progress</option> <option>Resolved</option> </select> <select className="border border-gray-300 rounded-lg px-3 py-2"> <option>All Priorities</option> <option>Urgent</option> <option>High</option> <option>Medium</option> <option>Low</option> </select> </div> </div> <TableComponent<Ticket> columns={["ID", "Title", "Customer", "Priority", "Status", "Assignee", "Created", "Last Reply", "Tags"]} data={tickets} props={["id", "title", "customer", "priority", "status", "assignee", "createdAt", "lastReply", "tags"]} formatValue={formatValue} sortableProps={["createdAt", "lastReply", "priority"]} enablePagination page={1} setPage={() => {}} itemsPerPage={20} /> </div> ); } ``` These examples demonstrate the flexibility and power of the nextjs-reusable-table component. You can mix and match features, customize styling, and adapt the component to fit any data display need in your application.