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.

Pro Tip: SmartTables works with your existing HTML tables and automatically adapts to different screen sizes by hiding less important columns on smaller screens.

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>
Important: Use the 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
Pro Tip: Users can search with operators like ">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 perPage values to limit the number of rows rendered at once
  • Use the loading.duration option 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>
    );
}
React-Specific Tips:
  • Use useRef to maintain a stable reference to the table element
  • Initialize the table in useEffect to 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
        };
    }
}
Vue.js-Specific Tips:
  • Use ref for template references
  • Initialize in onMounted hook
  • Clean up in onBeforeUnmount hook
  • 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();
    }
}
Angular-Specific Tips:
  • Use @ViewChild for template references
  • Initialize in ngOnInit lifecycle hook
  • Clean up in ngOnDestroy lifecycle 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
        }
    }
});
ASP.NET Core-Specific Tips:
  • 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
        }
    }
});
Laravel-Specific Tips:
  • 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')
        }
    }
});
Django-Specific Tips:
  • 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
        }
    }
});
Node.js/Express-Specific Tips:
  • Use Mongoose for MongoDB integration
  • Implement proper error handling middleware
  • Use JWT for API authentication
  • Consider using Redis for caching

Pro Tip: When integrating with any framework, always ensure proper cleanup by calling the 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

Common Issues:
  • Table not responsive? Make sure you've set data-priority attributes on your table headers.
  • Search not working as expected? Check your fuzzyMatch settings 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.