Data Table Generator
Create customizable data tables with sorting, filtering, pagination, and row selection.
Style
Size
Appearance
Features
Selection
Page Size
Colors
Themes
Preview
| Name | Role | Status | Joined | |
|---|---|---|---|---|
| John Doe | john@example.com | Admin | Active | 2023-01-15 |
| Jane Smith | jane@example.com | Editor | Active | 2023-02-20 |
| Bob Johnson | bob@example.com | Viewer | Inactive | 2023-03-10 |
| Alice Brown | alice@example.com | Editor | Active | 2023-04-05 |
| Charlie Wilson | charlie@example.com | Admin | Active | 2023-05-18 |
Page 1 of 2
HTML
<div class="data-table-container">
<div class="table-toolbar">
<input type="text" class="table-filter" placeholder="Search..." />
</div>
<div class="table-wrapper">
<table class="data-table striped hoverable bordered">
<thead>
<tr>
<th class="sortable">Name</th>
<th class="sortable">Email</th>
<th class="sortable">Role</th>
<th class="sortable">Status</th>
<th class="sortable">Joined</th>
</tr>
</thead>
<tbody>
<tr>
<td>John Doe</td>
<td>john@example.com</td>
<td>Admin</td>
<td><span class="status-badge active">Active</span></td>
<td>2023-01-15</td>
</tr>
<!-- More rows... -->
</tbody>
</table>
</div>
<div class="table-pagination">
<button class="page-btn" disabled>« Prev</button>
<span class="page-info">Page 1 of 2</span>
<button class="page-btn">Next »</button>
</div>
</div>CSS
.data-table-container {
background: #ffffff;
border-radius: 8px;
overflow: hidden;
}
.table-toolbar {
padding: 16px;
border-bottom: 1px solid #dee2e6;
}
.table-filter {
padding: 10px 16px;
border: 1px solid #dee2e6;
border-radius: 6px;
font-size: 14px;
width: 300px;
max-width: 100%;
background: #ffffff;
color: #333333;
}
.table-filter:focus {
outline: none;
border-color: #007bff;
}
.table-wrapper {
overflow-x: auto;
}
.table-wrapper.sticky-header {
max-height: 400px;
overflow-y: auto;
}
.table-wrapper.sticky-header thead {
position: sticky;
top: 0;
z-index: 1;
}
.data-table {
width: 100%;
border-collapse: collapse;
font-size: 14px;
color: #333333;
}
.data-table th,
.data-table td {
padding: 12px 16px;
text-align: left;
}
.data-table thead {
background: #f8f9fa;
}
.data-table th {
font-weight: 600;
white-space: nowrap;
}
.data-table th.sortable {
cursor: pointer;
user-select: none;
}
.data-table th.sortable:hover {
background: #e9ecef;
}
.data-table th .sort-icon {
margin-left: 8px;
opacity: 0.5;
}
.data-table th.sorted .sort-icon {
opacity: 1;
color: #007bff;
}
.data-table.bordered th,
.data-table.bordered td {
border: 1px solid #dee2e6;
}
.data-table:not(.bordered) tbody tr {
border-bottom: 1px solid #dee2e6;
}
.data-table.striped tbody tr:nth-child(even) {
background: #f8f9fa;
}
.data-table.hoverable tbody tr:hover {
background: #e9ecef;
}
.data-table .select-cell {
width: 40px;
text-align: center;
}
.data-table .row-number {
width: 50px;
text-align: center;
color: #999;
}
.data-table input[type="checkbox"],
.data-table input[type="radio"] {
width: 16px;
height: 16px;
cursor: pointer;
accent-color: #007bff;
}
.status-badge {
display: inline-block;
padding: 4px 10px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
}
.status-badge.active {
background: #dcfce7;
color: #166534;
}
.status-badge.inactive {
background: #fee2e2;
color: #991b1b;
}
.status-badge.pending {
background: #fef3c7;
color: #92400e;
}
.table-pagination {
display: flex;
align-items: center;
justify-content: center;
gap: 16px;
padding: 16px;
border-top: 1px solid #dee2e6;
}
.page-btn {
padding: 8px 16px;
border: 1px solid #dee2e6;
border-radius: 6px;
background: #ffffff;
color: #333333;
cursor: pointer;
font-size: 14px;
transition: all 0.2s;
}
.page-btn:hover:not(:disabled) {
background: #e9ecef;
border-color: #007bff;
}
.page-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.page-info {
font-size: 14px;
color: #333333;
}JavaScript
class DataTable {
constructor(container, options = {}) {
this.container = container;
this.options = {
data: [],
columns: [],
sortable: true,
filterable: true,
pagination: true,
pageSize: 5,
selectable: false,
selectType: 'checkbox',
...options
};
this.currentPage = 1;
this.sortColumn = null;
this.sortDirection = 'asc';
this.selectedRows = new Set();
this.filterText = '';
this.init();
}
init() {
this.render();
this.bindEvents();
}
bindEvents() {
// Filter input
const filterInput = this.container.querySelector('.table-filter');
if (filterInput) {
filterInput.addEventListener('input', (e) => {
this.filterText = e.target.value;
this.currentPage = 1;
this.render();
});
}
// Sortable headers
this.container.querySelectorAll('th.sortable').forEach((th, index) => {
th.addEventListener('click', () => {
const column = this.options.columns[index].key;
if (this.sortColumn === column) {
this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc';
} else {
this.sortColumn = column;
this.sortDirection = 'asc';
}
this.render();
});
});
// Pagination
this.container.addEventListener('click', (e) => {
if (e.target.classList.contains('page-prev')) {
this.currentPage = Math.max(1, this.currentPage - 1);
this.render();
} else if (e.target.classList.contains('page-next')) {
this.currentPage = Math.min(this.getTotalPages(), this.currentPage + 1);
this.render();
}
});
// Row selection
this.container.addEventListener('change', (e) => {
if (e.target.classList.contains('select-all')) {
this.toggleAllRows(e.target.checked);
} else if (e.target.classList.contains('row-select')) {
const rowId = e.target.closest('tr').dataset.id;
this.toggleRow(rowId);
}
});
}
getFilteredData() {
if (!this.filterText) return this.options.data;
return this.options.data.filter(row =>
Object.values(row).some(value =>
String(value).toLowerCase().includes(this.filterText.toLowerCase())
)
);
}
getSortedData() {
const filtered = this.getFilteredData();
if (!this.sortColumn) return filtered;
return [...filtered].sort((a, b) => {
const aVal = a[this.sortColumn];
const bVal = b[this.sortColumn];
if (aVal < bVal) return this.sortDirection === 'asc' ? -1 : 1;
if (aVal > bVal) return this.sortDirection === 'asc' ? 1 : -1;
return 0;
});
}
getPaginatedData() {
const sorted = this.getSortedData();
if (!this.options.pagination) return sorted;
const start = (this.currentPage - 1) * this.options.pageSize;
return sorted.slice(start, start + this.options.pageSize);
}
getTotalPages() {
return Math.ceil(this.getSortedData().length / this.options.pageSize);
}
toggleRow(id) {
if (this.options.selectType === 'radio') {
this.selectedRows.clear();
this.selectedRows.add(id);
} else {
if (this.selectedRows.has(id)) {
this.selectedRows.delete(id);
} else {
this.selectedRows.add(id);
}
}
this.render();
}
toggleAllRows(selected) {
if (selected) {
this.getPaginatedData().forEach(row => this.selectedRows.add(row.id));
} else {
this.selectedRows.clear();
}
this.render();
}
getSelectedRows() {
return Array.from(this.selectedRows);
}
render() {
// Re-render the table with current state
// Implementation depends on your template system
}
}
// Usage
const table = new DataTable(document.querySelector('.data-table-container'), {
data: yourData,
columns: [
{ key: 'name', label: 'Name' },
{ key: 'email', label: 'Email' },
// ...
]
});React Component
import React, { useState, useMemo } from 'react';
import './DataTable.css';
const DataTable = ({
data = [],
columns = [],
sortable = true,
filterable = true,
pagination = true,
pageSize = 5,
selectable = false,
selectType = 'checkbox',
striped = true,
hoverable = true,
showRowNumbers = false
}) => {
const [sortColumn, setSortColumn] = useState(null);
const [sortDirection, setSortDirection] = useState('asc');
const [filterText, setFilterText] = useState('');
const [currentPage, setCurrentPage] = useState(1);
const [selectedRows, setSelectedRows] = useState([]);
const handleSort = (column) => {
if (!sortable) return;
if (sortColumn === column) {
setSortDirection(prev => prev === 'asc' ? 'desc' : 'asc');
} else {
setSortColumn(column);
setSortDirection('asc');
}
};
const filteredData = useMemo(() => {
if (!filterText) return data;
return data.filter(row =>
Object.values(row).some(value =>
String(value).toLowerCase().includes(filterText.toLowerCase())
)
);
}, [data, filterText]);
const sortedData = useMemo(() => {
if (!sortColumn) return filteredData;
return [...filteredData].sort((a, b) => {
const aVal = a[sortColumn];
const bVal = b[sortColumn];
if (aVal < bVal) return sortDirection === 'asc' ? -1 : 1;
if (aVal > bVal) return sortDirection === 'asc' ? 1 : -1;
return 0;
});
}, [filteredData, sortColumn, sortDirection]);
const paginatedData = useMemo(() => {
if (!pagination) return sortedData;
const start = (currentPage - 1) * pageSize;
return sortedData.slice(start, start + pageSize);
}, [sortedData, currentPage, pageSize, pagination]);
const totalPages = Math.ceil(sortedData.length / pageSize);
const toggleRowSelection = (id) => {
if (selectType === 'radio') {
setSelectedRows([id]);
} else {
setSelectedRows(prev =>
prev.includes(id)
? prev.filter(r => r !== id)
: [...prev, id]
);
}
};
const toggleAllRows = () => {
if (selectedRows.length === paginatedData.length) {
setSelectedRows([]);
} else {
setSelectedRows(paginatedData.map(r => r.id));
}
};
return (
<div className="data-table-container">
{filterable && (
<div className="table-toolbar">
<input
type="text"
className="table-filter"
placeholder="Search..."
value={filterText}
onChange={(e) => {
setFilterText(e.target.value);
setCurrentPage(1);
}}
/>
</div>
)}
<div className="table-wrapper">
<table className={`data-table ${striped ? 'striped' : ''} ${hoverable ? 'hoverable' : ''}`}>
<thead>
<tr>
{selectable && (
<th className="select-cell">
{selectType === 'checkbox' && (
<input
type="checkbox"
checked={selectedRows.length === paginatedData.length}
onChange={toggleAllRows}
/>
)}
</th>
)}
{showRowNumbers && <th className="row-number">#</th>}
{columns.map(col => (
<th
key={col.key}
className={sortable ? 'sortable' : ''}
onClick={() => handleSort(col.key)}
>
{col.label}
{sortColumn === col.key && (
<span className="sort-icon">
{sortDirection === 'asc' ? 'โฒ' : 'โผ'}
</span>
)}
</th>
))}
</tr>
</thead>
<tbody>
{paginatedData.map((row, index) => (
<tr key={row.id}>
{selectable && (
<td className="select-cell">
<input
type={selectType}
name="row-select"
checked={selectedRows.includes(row.id)}
onChange={() => toggleRowSelection(row.id)}
/>
</td>
)}
{showRowNumbers && (
<td className="row-number">
{(currentPage - 1) * pageSize + index + 1}
</td>
)}
{columns.map(col => (
<td key={col.key}>{row[col.key]}</td>
))}
</tr>
))}
</tbody>
</table>
</div>
{pagination && totalPages > 1 && (
<div className="table-pagination">
<button
className="page-btn"
disabled={currentPage === 1}
onClick={() => setCurrentPage(p => p - 1)}
>
« Prev
</button>
<span className="page-info">
Page {currentPage} of {totalPages}
</span>
<button
className="page-btn"
disabled={currentPage === totalPages}
onClick={() => setCurrentPage(p => p + 1)}
>
Next »
</button>
</div>
)}
</div>
);
};
export default DataTable;Export Data