SmartTables.js
Smarter Tables, Less Hassle!
SmartTables.js is a powerful, feature-rich JavaScript plugin designed to enhance HTML tables with advanced functionality such as sorting, searching, pagination, responsive behavior, and data export capabilities. It is built with modern web development practices in mind, offering a flexible and extensible API for developers to customize and extend its functionality.
This plugin is ideal for developers who need to manage large datasets in a user-friendly way, providing a seamless experience for end-users while maintaining a high level of customization and control.
Installation
Include the SmartTables.js script in your HTML file:
<script src="path/to/smartTables.js"></script>
Basic initialization
// Initialize with default options
var myTable = new SmartTables('tableId');
// Or with custom options
var myTable = new SmartTables('tableId', {
perPage: 15,
search: true,
sort: true,
pagination: true,
export: true
});
HTML
<table id="myTable" class="table">
<thead>
<tr>
<th data-priority="1">ID</th>
<th data-priority="2">Name</th>
<th data-priority="3">Position</th>
<th data-priority="4">Office</th>
<th data-priority="5">Age</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>John Doe</td>
<td>Developer</td>
<td>New York</td>
<td>32</td>
</tr>
<!-- More rows... -->
</tbody>
</table>
data-priority attribute on table headers to control which columns are hidden first when the table becomes too narrow for the viewport.
Configuration Options
Extensive configuration options to customize its behavior
| Option | Default Value | Description | Use Case | Type |
|---|---|---|---|---|
perPage |
10 | Number of rows to display per page in pagination. | Controls how many rows are shown before requiring pagination navigation. | number |
search |
true | Enables or disables the search functionality for the table. | If set to false, the search input in the toolbar will be hidden or disabled. | boolean |
sort |
true | Enables or disables sorting functionality for table columns. | If set to false, column headers won't be clickable for sorting, and sorting classes won't be applied. | boolean |
pagination |
true | Enables or disables pagination controls for the table. | If set to false, the table will display all rows without pagination, and the pagination UI will be hidden. | boolean |
export |
true | Enables or disables export functionality (e.g., Excel, CSV, Copy) for the table. | If set to false, export buttons in the toolbar will be hidden or disabled. | boolean |
loading.enabled |
true | Enables or disables the loading spinner/indicator during data loading or processing. | Set to false to disable visual feedback during long operations. | boolean |
loading.duration |
0 | Duration (in milliseconds) of an artificial delay for the loading indicator. | Useful for testing or ensuring the loading UI is visible for UX purposes, even for fast operations. | number |
loading.minDuration |
300 | Minimum duration (in milliseconds) to display the loading indicator, even if the operation completes faster. | Ensures users see the loading UI for at least 300ms, improving perceived performance and UX. | number |
responsive.enabled |
true | Enables or disables responsive behavior (e.g., hiding columns on small screens). | Set to false to disable responsive adjustments, keeping all columns visible regardless of screen size. | boolean |
responsive.breakpoint |
768 | Breakpoint (in pixels) at which responsive behavior starts (e.g., hiding columns). | Customize to match your design's breakpoint needs (e.g., 600 for mobile-first designs). | number |
responsive.columnPriorities.0 |
1 | Priority for the ID column (highest priority, stays visible longest). | Adjust priorities to control visibility order (e.g., keep ID and Name visible longer). | number |
responsive.columnPriorities.1 |
2 | Priority for the Name column (second-highest priority). | Adjust priorities to control visibility order (e.g., keep ID and Name visible longer). | number |
responsive.columnPriorities.2 |
3 | Priority for the Position column. | Adjust priorities to control visibility order (e.g., keep ID and Name visible longer). | number |
responsive.columnPriorities.3 |
4 | Priority for the Office column. | Adjust priorities to control visibility order (e.g., keep ID and Name visible longer). | number |
responsive.columnPriorities.4 |
5 | Priority for the Age column. | Adjust priorities to control visibility order (e.g., keep ID and Name visible longer). | number |
responsive.columnPriorities.5 |
6 | Priority for the Start Date column (lowest priority, hides first). | Adjust priorities to control visibility order (e.g., keep ID and Name visible longer). | number |
responsive.details.type |
'column' | Type of detail display for hidden columns (e.g., 'column' for a control column). | Set to 'inline' or custom types to change how hidden data is shown (e.g., in a modal). | string |
responsive.details.target |
0 | Target column index for the detail control (e.g., first column, index 0). | Customize to use a different column for expand/collapse buttons (e.g., 1 for Name column). | number |
debug |
false | Enables or disables debug logging for development and troubleshooting. | Set to true to log detailed information (e.g., widths, errors) to the console for debugging. | boolean |
fuzzyMatch.threshold |
0.7 | Threshold for fuzzy matching (0.0–1.0), where 1.0 is a perfect match and lower values allow more leniency. | Adjust to make searches more or less strict (e.g., 0.5 for very lenient, 0.9 for strict). | number |
fuzzyMatch.minMatchLength |
2 | Minimum number of characters required in a search term for fuzzy matching to apply. | Prevents fuzzy matching on very short searches (e.g., single characters), improving performance. | number |
fuzzyMatch.multiWordThreshold |
0.5 | Threshold for multi-word search matches (0.0–1.0), determining how well multiple words must match. | Lower values allow looser multi-word matches (e.g., partial matches across words). | number |
fuzzyMatch.maxDistance |
2 | Maximum Levenshtein distance (number of single-character edits) allowed for typo tolerance in fuzzy matching. | Controls how many typos or character differences are tolerated (e.g., 1 for strict, 3 for lenient). | number |
classes.wrapper |
'st-wrapper' | Class for the wrapper div containing the table, toolbar, and pagination. | Customize to apply additional styling or integrate with existing classes. | string |
classes.table |
'st-table table table-striped table-hover' | Class for the table element, including Bootstrap classes for styling (striped, hover). | Modify to change table appearance (e.g., remove hover effects, add borders). | string |
classes.toolbar |
'st-toolbar d-flex justify-content-between mb-3' | Class for the toolbar containing search, export, and other controls. | Customize for layout or spacing adjustments (e.g., add margins, change flex alignment). | string |
classes.search |
'st-search form-control' | Class for the search input field in the toolbar. | Adjust to match your input styling or add Bootstrap variants (e.g., 'form-control-lg'). | string |
classes.pagination |
'st-pagination pagination justify-content-center' | Class for the pagination controls. | Customize for alignment, size, or styling (e.g., add 'pagination-sm' for smaller pagination). | string |
classes.export |
'st-export btn-group' | Class for the export button group in the toolbar. | Modify to adjust button group styling or integrate with custom button classes. | string |
data.type |
null | Type of data source for the table ('json', 'csv', 'ajax', or null for DOM-based tables). | Set to specify how data is loaded (e.g., 'ajax' for remote API, 'json' for local JSON). | string|null |
data.source |
null | Source of the data (URL, string, or object depending on type). | Provide a URL for AJAX/CSV/JSON, a string for inline CSV, or an object for JSON data. | string|null|Object |
data.columns |
[] | Array of column definitions specifying data fields and display properties. | Define column titles, data keys, rendering functions, and classes for each column. | Array |
data.processing |
false | Enables or disables a processing indicator during data operations. | Set to true to show a processing state (e.g., "Processing..." text) during data loading. | boolean |
data.serverSide |
false | Enables or disables server-side processing for large datasets. | Set to true for AJAX-based tables with server-side pagination, sorting, and filtering. | boolean |
data.method |
'GET' | HTTP method for AJAX requests (e.g., 'GET', 'POST'). | Customize for API requirements (e.g., 'POST' for JSON payloads). | string |
data.headers |
{} | Custom headers for AJAX requests. | Add authentication tokens, content types, or other headers for API calls. | Object |
data.params |
{} | Additional parameters for AJAX requests. | Pass query parameters or data for server-side processing (e.g., filters, pagination). | Object |
data.parser |
null | Custom parser function for transforming raw data before processing. | Implement to format or validate data (e.g., convert CSV to JSON, normalize fields). | Function|null |
hooks.beforeInit |
null | Called before the table is initialized. | Perform setup or validation before table creation. | Function|null |
hooks.afterInit |
null | Called after the table is fully initialized. | Initialize custom UI, bind events, or log initialization completion. | Function|null |
hooks.beforeDestroy |
null | Called before the table is destroyed. | Clean up resources or prompt user confirmation before destruction. | Function|null |
hooks.afterDestroy |
null | Called after the table is destroyed. | Perform final cleanup or reset external state after destruction. | Function|null |
hooks.beforeDataLoad |
null | Called before data is loaded from a source (e.g., JSON, CSV, AJAX). | Modify data source, add authentication, or validate data before loading. | Function|null |
hooks.afterDataLoad |
null | Called after data is loaded but before it's processed or rendered. | Transform, validate, or filter data before table rendering. | Function|null |
hooks.beforeDraw |
null | Called before the table is redrawn (e.g., after filtering, sorting). | Prepare data or UI before redraw, or prevent unnecessary redraws. | Function|null |
hooks.afterDraw |
null | Called after the table is redrawn. | Update custom UI, log redraw completion, or trigger animations. | Function|null |
hooks.beforeEdit |
null | Called before a row is edited (if editing is implemented). | Validate row data or show custom edit UI before changes. | Function|null |
hooks.afterEdit |
null | Called after a row is edited but before saving (if editing is implemented). | Update UI, log changes, or prepare for saving. | Function|null |
hooks.beforeSave |
null | Called before changes to a row are saved (if editing is implemented). | Validate or transform data before saving to the table or server. | Function|null |
hooks.afterSave |
null | Called after changes to a row are saved (if editing is implemented). | Update UI, sync with server, or log save completion. | Function|null |
hooks.onSort |
null | Called when a column is sorted. | Log sorting, update UI, or trigger custom sorting logic. | Function|null |
hooks.onFilter |
null | Called when the table is filtered via search. | Log filters, update external state, or customize filtering logic. | Function|null |
hooks.onPaginate |
null | Called when the pagination page changes. | Log page changes, update UI, or trigger server-side pagination. | Function|null |
hooks.onExport |
null | Called when data is exported (e.g., to Excel, CSV, Copy). | Modify export data, log exports, or customize output format. | Function|null |
hooks.onImport |
null | Called when data is imported (e.g., from CSV or JSON). | Validate imported data, log imports, or trigger custom processing. | Function|null |
hooks.onResize |
null | Called when the table's responsive layout changes due to resizing. | Adjust custom UI, log resize events, or trigger animations. | Function|null |
plugins |
[] | Array of plugin objects to extend the functionality of SmartTables. | Add custom plugins for features like charting, advanced filtering, or custom rendering. | Array |
Advanced Configuration Example
Here's an example of how to configure SmartTables with advanced options:
var myTable = new SmartTables('tableId', {
perPage: 25,
search: true,
sort: true,
pagination: true,
export: true,
loading: {
enabled: true,
duration: 500, // Show loading for at least 500ms
minDuration: 300
},
responsive: {
enabled: true,
breakpoint: 992,
columnPriorities: {
0: 1, // ID column - highest priority
1: 2, // Name column - second highest priority
2: 3, // Position
3: 4, // Office
4: 5 // Age - lowest priority
}
},
debug: false,
fuzzyMatch: {
threshold: 0.6, // Lower threshold = more matches
minMatchLength: 2, // Minimum characters to match
multiWordThreshold: 0.5,
maxDistance: 3 // Higher distance = more tolerance for typos
}
});
Data Loading Methods
SmartTables supports various data loading methods:
// Load from JSON data
var myTable = new SmartTables('tableId', {
data: {
type: 'json',
source: [
{ id: 1, name: 'John Doe', position: 'Developer', office: 'New York', age: 32 },
{ id: 2, name: 'Jane Smith', position: 'Designer', office: 'London', age: 28 }
],
columns: [
{ data: 'id', title: 'ID' },
{ data: 'name', title: 'Name' },
{ data: 'position', title: 'Position' },
{ data: 'office', title: 'Office' },
{ data: 'age', title: 'Age' }
]
}
});
// Load from AJAX
var myTable = new SmartTables('tableId', {
data: {
type: 'ajax',
source: 'api/users',
method: 'GET',
headers: { 'Authorization': 'Bearer token123' },
params: { limit: 100 }
}
});
// Load from CSV
var myTable = new SmartTables('tableId', {
data: {
type: 'csv',
source: 'data/employees.csv'
}
});
Event Hooks
SmartTables provides hooks for various events in the table lifecycle:
var myTable = new SmartTables('tableId', {
hooks: {
// Table lifecycle hooks
beforeInit: function(instance) {
console.log('Before table initialization');
},
afterInit: function(instance) {
console.log('Table initialized!');
},
// Data hooks
beforeDataLoad: function(data, instance) {
console.log('About to load data');
},
afterDataLoad: function(data, instance) {
console.log('Data loaded successfully');
},
// Action hooks
onSort: function(column, direction, instance) {
console.log('Table sorted by column', column, 'in', direction, 'direction');
},
onFilter: function(searchTerm, filteredRows, instance) {
console.log('Table filtered with term:', searchTerm);
},
onPaginate: function(pageNumber, instance) {
console.log('Page changed to', pageNumber);
}
}
});
Search Capabilities
SmartTables includes powerful search capabilities with support for:
- Fuzzy matching: Finds results even with typos or partial matches
- Special data types: Intelligently searches dates, numbers, emails, phone numbers
- Comparison operators: Support for
>,<,=with numeric values - Multi-word search: Matches records containing any or all search terms
>30" to find all numeric values greater than 30, or use "@gmail.com" to find all Gmail addresses.
Responsive Behavior
SmartTables automatically adapts to different screen sizes by:
- Hiding less important columns on smaller screens based on priority
- Providing an expand/collapse interface to view hidden column data
- Automatically measuring and optimizing column widths
<!-- Set column priorities with data attributes -->
<th data-priority="1">ID</th> <!-- Highest priority (last to hide) -->
<th data-priority="2">Name</th>
<th data-priority="3">Position</th>
<th data-priority="4">Office</th>
<th data-priority="5">Age</th> <!-- Lowest priority (first to hide) -->
<!-- Force columns to always remain visible -->
<th class="always-visible">Actions</th>
Export Options
SmartTables provides built-in export functionality:
- Excel: Export to XLSX format
- CSV: Export to CSV format
- Copy: Copy table data to clipboard
// Get table instance
var table = document.getElementById('myTable').__smartTable;
// Export to different formats
table.exportData('excel');
table.exportData('csv');
table.exportData('copy');
API Methods
SmartTables exposes several methods for programmatic control:
// Get table instance
var table = document.getElementById('myTable').__smartTable;
// Redraw the table
table.draw();
// Sort by column
table.sortBy(2, 'asc'); // Sort by 3rd column ascending
// Filter the table
table.handleSearch('developer');
// Export data
table.exportData('excel');
// Hide/show columns
table.hideColumn(3);
table.showColumn(3);
// Destroy the instance
table.destroy();
Plugin System
SmartTables supports plugins to extend its functionality:
// Define a plugin
var myPlugin = {
name: 'myPlugin',
init: function() {
console.log('Plugin initialized for table:', this.instance.table.id);
},
afterDraw: function() {
console.log('Table was redrawn');
}
};
// Initialize table with the plugin
var myTable = new SmartTables('tableId', {
plugins: [myPlugin]
});
Performance Tips
- For large datasets (1000+ rows), consider using server-side processing
- Set appropriate
perPagevalues to limit the number of rows rendered at once - Use the
loading.durationoption to show a loading indicator for long operations - Disable features you don't need (search, sort, pagination) for simpler tables
Framework Integrations
SmartTables.js can be seamlessly integrated with various modern frameworks and backend technologies. Here's how to implement it in different environments:
React Integration
import { useEffect, useRef } from 'react';
import SmartTables from 'smarttables';
function DataTable({ data }) {
const tableRef = useRef(null);
useEffect(() => {
if (tableRef.current) {
const table = new SmartTables(tableRef.current, {
data: {
type: 'json',
source: data,
columns: [
{ data: 'id', title: 'ID' },
{ data: 'name', title: 'Name' },
// ... other columns
]
}
});
// Cleanup on unmount
return () => table.destroy();
}
}, [data]);
return (
<table ref={tableRef} className="table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
{/* ... other headers */}
</tr>
</thead>
<tbody>
{/* Data will be populated by SmartTables */}
</tbody>
</table>
);
}
- Use
useRefto maintain a stable reference to the table element - Initialize the table in
useEffectto ensure the DOM is ready - Clean up the table instance in the effect's cleanup function
- Consider using React's Context API for global table state management
Vue.js Integration
import { onMounted, onBeforeUnmount, ref } from 'vue';
import SmartTables from 'smarttables';
export default {
setup() {
const tableRef = ref(null);
let table = null;
onMounted(() => {
if (tableRef.value) {
table = new SmartTables(tableRef.value, {
data: {
type: 'json',
source: props.data,
columns: [
{ data: 'id', title: 'ID' },
{ data: 'name', title: 'Name' },
// ... other columns
]
}
});
}
});
onBeforeUnmount(() => {
if (table) table.destroy();
});
return {
tableRef
};
}
}
- Use
reffor template references - Initialize in
onMountedhook - Clean up in
onBeforeUnmounthook - Consider using Vuex/Pinia for state management
Angular Integration
import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core';
import SmartTables from 'smarttables';
@Component({
selector: 'app-data-table',
template: `
<table dataTable class="table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<!-- ... other headers -->
</tr>
</thead>
<tbody>
<!-- Data will be populated by SmartTables -->
</tbody>
</table>
`
})
export class DataTableComponent implements OnInit, OnDestroy {
@ViewChild('dataTable') tableRef: ElementRef;
private table: any;
ngOnInit() {
if (this.tableRef.nativeElement) {
this.table = new SmartTables(this.tableRef.nativeElement, {
data: {
type: 'json',
source: this.data,
columns: [
{ data: 'id', title: 'ID' },
{ data: 'name', title: 'Name' },
// ... other columns
]
}
});
}
}
ngOnDestroy() {
if (this.table) this.table.destroy();
}
}
- Use
@ViewChildfor template references - Initialize in
ngOnInitlifecycle hook - Clean up in
ngOnDestroylifecycle hook - Consider using NgRx for state management
ASP.NET Core Integration
// Controller
public class DataController : ControllerBase
{
[HttpGet("api/data")]
public async Task GetData([FromQuery] DataTableRequest request)
{
var data = await _service.GetDataAsync();
// Apply server-side processing
var result = data
.Skip(request.Start)
.Take(request.Length)
.ToList();
return Ok(new {
draw = request.Draw,
recordsTotal = data.Count,
recordsFiltered = data.Count,
data = result
});
}
}
// JavaScript
const table = new SmartTables('myTable', {
data: {
type: 'ajax',
source: '/api/data',
serverSide: true,
method: 'GET',
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
}
}
});
- Use dependency injection for services
- Implement proper error handling and logging
- Consider using Entity Framework Core for data access
- Use middleware for authentication and authorization
PHP/Laravel Integration
// Controller
public function getData(Request $request)
{
$query = User::query();
// Handle search
if ($request->has('search')) {
$query->where('name', 'like', "%{$request->search}%");
}
// Handle sorting
if ($request->has('order')) {
$column = $request->input('order.0.column');
$direction = $request->input('order.0.dir');
$query->orderBy($columns[$column], $direction);
}
// Handle pagination
$data = $query->skip($request->input('start'))
->take($request->input('length'))
->get();
return response()->json([
'draw' => $request->input('draw'),
'recordsTotal' => User::count(),
'recordsFiltered' => $query->count(),
'data' => $data
]);
}
// JavaScript
const table = new SmartTables('myTable', {
data: {
type: 'ajax',
source: '/api/data',
serverSide: true,
method: 'GET',
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
}
}
});
- Use Laravel's Eloquent ORM for efficient queries
- Implement proper validation using Form Requests
- Use Laravel's caching for better performance
- Consider using Laravel Sanctum for API authentication
Django Integration
# views.py
from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
from django.core.paginator import Paginator
@require_http_methods(["GET"])
def get_data(request):
queryset = User.objects.all()
// Handle search
search = request.GET.get('search[value]')
if search:
queryset = queryset.filter(name__icontains=search)
// Handle sorting
order_column = request.GET.get('order[0][column]')
order_dir = request.GET.get('order[0][dir]')
if order_column and order_dir:
queryset = queryset.order_by(f"{order_column} {order_dir}")
// Handle pagination
start = int(request.GET.get('start', 0))
length = int(request.GET.get('length', 10))
paginator = Paginator(queryset, length)
page = (start // length) + 1
return JsonResponse({
'draw': int(request.GET.get('draw', 1)),
'recordsTotal': User.objects.count(),
'recordsFiltered': queryset.count(),
'data': list(paginator.get_page(page))
})
# JavaScript
const table = new SmartTables('myTable', {
data: {
type: 'ajax',
source: '/api/data/',
serverSide: true,
method: 'GET',
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
}
});
- Use Django REST framework for API endpoints
- Implement proper caching with Django's cache framework
- Use Django's built-in CSRF protection
- Consider using Django Channels for real-time updates
Node.js/Express Integration
// Controller
const getData = async (req, res) => {
try {
const { start, length, search, order } = req.query;
// Build query
let query = User.find();
// Handle search
if (search) {
query = query.find({
$or: [
{ name: { $regex: search, $options: 'i' } },
{ email: { $regex: search, $options: 'i' } }
]
});
}
// Handle sorting
if (order) {
const [column, direction] = order.split(':');
query = query.sort({ [column]: direction === 'asc' ? 1 : -1 });
}
// Handle pagination
const data = await query
.skip(parseInt(start))
.limit(parseInt(length))
.exec();
const total = await User.countDocuments();
const filtered = await query.countDocuments();
res.json({
draw: parseInt(req.query.draw),
recordsTotal: total,
recordsFiltered: filtered,
data
});
} catch (error) {
res.status(500).json({ error: error.message });
}
};
// JavaScript
const table = new SmartTables('myTable', {
data: {
type: 'ajax',
source: '/api/data',
serverSide: true,
method: 'GET',
headers: {
'Authorization': 'Bearer ' + token
}
}
});
- Use Mongoose for MongoDB integration
- Implement proper error handling middleware
- Use JWT for API authentication
- Consider using Redis for caching
destroy() method when the component is unmounted or the table is no longer needed. This prevents memory leaks and ensures proper event cleanup.
Framework-Specific Considerations:
- React/Vue/Angular: Use framework-specific lifecycle hooks to initialize and cleanup the table instance.
- Backend Frameworks: Implement proper server-side processing to handle large datasets efficiently.
- State Management: Consider using your framework's state management solution (Redux, Vuex, etc.) to handle table data and state.
- CSRF Protection: Include appropriate CSRF tokens in AJAX requests when required by your backend framework.
- Error Handling: Implement proper error handling both on the frontend and backend.
- Performance: Use appropriate caching strategies and optimize database queries.
- Security: Implement proper authentication and authorization mechanisms.
- Testing: Write unit tests for both frontend and backend components.
Troubleshooting
- Table not responsive? Make sure you've set
data-priorityattributes on your table headers. - Search not working as expected? Check your
fuzzyMatchsettings and try adjusting the threshold. - Export not working? Ensure you have the required dependencies for Excel export.
- Performance issues? Try reducing the number of rows per page or use server-side processing.