Version: 1.6.0
Latest Update: July 2025
Status: ✅ Production Ready
[@livewire](https://github.com/livewire)('aftable', [
'model' => 'App\Models\Item',
'columns' => [
['key' => 'name', 'label' => 'Item Name'],
['key' => 'price', 'label' => 'Price'],
['key' => 'created_at', 'label' => 'Created'],
],
])
[@livewire](https://github.com/livewire)('aftable', [
'model' => 'App\Models\Item',
'columns' => [
['key' => 'name', 'label' => 'Item Name'],
['key' => 'price', 'label' => 'Price'],
['key' => 'created_at', 'label' => 'Created'],
],
'sortBy' => 'created_at',
'sort' => 'desc',
])
Pass any PHP array or Laravel Collection directly — no Eloquent model required. Best for: API responses, computed/aggregated data, small datasets.
[@php](https://github.com/php)
$employees = [
['name' => 'Alice', 'salary' => 5000, 'dept' => 'Engineering'],
['name' => 'Bob', 'salary' => 4500, 'dept' => 'Design'],
];
[@endphp](https://github.com/endphp)
[@livewire](https://github.com/livewire)('aftable', [
'data' => $employees,
'vars' => ['currency' => 'USD'],
'columns' => [
['key' => 'name', 'label' => 'Name'],
['key' => 'dept', 'label' => 'Department'],
['key' => 'salary', 'label' => 'Salary',
'raw' => '{{ $currency }} {{ number_format($row->salary, 2) }}'],
],
])
Static mode features: in-memory search, sort, and pagination — fully reactive.
Render the table using your app's own table markup instead of the default Bootstrap one.
[@aftable](https://github.com/aftable) / [@endaftable](https://github.com/endaftable) directives[@aftable](https://github.com/aftable)(['model' => App\Models\User::class, 'columns' => [...]])
<table class="table table-hover table-striped my-theme-class">
<thead class="bg-dark text-white"><tr></tr></thead>
<tbody></tbody>
</table>
[@endaftable](https://github.com/endaftable)
The component extracts your <table> tag's attributes and applies them while still rendering all the standard headers, rows, search, filters, and pagination.
customTemplate parameter directly[@livewire](https://github.com/livewire)('aftable', [
'model' => App\Models\User::class,
'columns' => [...],
'customTemplate' => '<table class="table table-hover my-theme" id="myTable">',
])
['key' => 'status', 'label' => 'Status',
'raw' => '<span class="badge bg-{{ $row->active ? \'success\' : \'danger\' }}">
{{ $row->active ? \'Active\' : \'Inactive\' }}
</span>'],
['key' => 'status', 'label' => 'Status',
'raw' => fn($row) => $row->active
? '<span class="badge bg-success">Active</span>'
: '<span class="badge bg-danger">Inactive</span>'],
// With extra vars ($vars is your 'vars' array)
['key' => 'price', 'label' => 'Price',
'raw' => fn($row, $vars) => e($vars['currency']).' '.number_format($row->price, 2)],
['key' => 'total', 'label' => 'Total', 'sortable' => false,
'raw' => '{{ number_format($row->price * $row->qty, 2) }}'],
The classCondition map now uses closures or safe shorthand (no more eval()):
'classCondition' => [
// Closure (most flexible)
'text-success fw-bold' => fn($row) => $row->active,
// Boolean literal
'opacity-50' => false,
// Truthy shorthand — truthy check: !!$row->featured
'border-primary' => 'featured',
// Negation shorthand — !$row->active
'text-muted' => '!active',
// Equality shorthand — $row->status === 'inactive'
'text-danger' => 'status:inactive',
],
| Parameter | Type | Default | Description |
|---|---|---|---|
model |
class-string | null |
Eloquent model class (Eloquent mode) |
data |
array|Collection | null |
Static rows (static mode) |
vars |
array | [] |
Extra variables available in all raw templates |
columns |
array | [] |
Column definitions |
filters |
array | [] |
Filter configuration |
actions |
array | [] |
Row action buttons |
sortBy |
string | null |
Default sort column |
sort |
string | 'desc' |
Default sort direction |
customTemplate |
string | null |
Custom <table> HTML for theming |
query |
array|Closure | null |
Extra query constraints |
searchable |
bool | true |
Enable global search |
exportable |
bool | false |
Show export dropdown |
printable |
bool | false |
Show print button |
index |
bool | false |
Show row index column |
checkbox |
bool | false |
Row selection checkboxes |
colvisBtn |
bool | true |
Column visibility toggle |
refreshBtn |
bool | false |
Manual refresh button |
records |
int | 10 |
Rows per page |
dateColumn |
string | null |
Column for date range filter |
[@livewire](https://github.com/livewire)('aftable', [
'model' => 'App\Models\Item',
'columns' => [
['key' => 'name', 'label' => 'Item Name'],
['key' => 'category_name', 'label' => 'Category', 'relation' => 'category:name'],
['key' => 'reviews_count', 'label' => 'Reviews'],
],
'sortBy' => 'category_name',
'sortDirection' => 'asc',
])
[@livewire](https://github.com/livewire)('aftable', [
'model' => 'App\Models\AccountFlow\Transaction',
'columns' => [
['key' => 'date', 'label' => 'Date'],
['key' => 'amount', 'label' => 'Amount'],
['key' => 'type', 'label' => 'Type'],
['key' => 'category_id', 'relation' => 'category:name', 'label' => 'Category'],
['key' => 'account_id', 'relation' => 'account:name', 'label' => 'Account'],
['key' => 'description', 'label' => 'Description'],
],
'sortBy' => 'date',
'sortDirection' => 'desc', // Newest transactions first
'records' => 25,
])
[@livewire](https://github.com/livewire)('aftable', [
'model' => 'App\Models\Product',
'columns' => [
['key' => 'sku', 'label' => 'SKU'],
['key' => 'name', 'label' => 'Product'],
['key' => 'category_name', 'label' => 'Category', 'relation' => 'category:name'],
['key' => 'price', 'label' => 'Price'],
['key' => 'stock', 'label' => 'Stock'],
['key' => 'reviews_count', 'label' => 'Reviews'],
],
'sortBy' => 'price',
'sortDirection' => 'desc', // Most expensive first
'searchable' => true,
'showExport' => true,
])
[@livewire](https://github.com/livewire)('aftable', [
'model' => 'App\Models\User',
'columns' => [
['key' => 'name', 'label' => 'Full Name'],
['key' => 'email', 'label' => 'Email'],
['key' => 'role', 'label' => 'Role'],
['key' => 'organization_name', 'label' => 'Organization', 'relation' => 'organization:name'],
['key' => 'created_at', 'label' => 'Member Since'],
],
'sortBy' => 'name',
'sortDirection' => 'asc', // A to Z
'records' => 50,
])
'sortBy' => 'column_key', // Initial sort column
'sortDirection' => 'asc|desc', // Initial direction
'columns' => [
['key' => 'customer_name', 'label' => 'Customer', 'relation' => 'customer:name'],
],
'sortBy' => 'customer_name', // Sorts by customer.name via JOIN
'columns' => [
['key' => 'items_count', 'label' => 'Items'], // Auto-detected
],
'sortBy' => 'items_count', // Sorts by COUNT(items)
| Type | Sortable | Example |
|---|---|---|
| Database Column | ✅ | ['key' => 'name'] |
| Simple Relation | ✅ | ['relation' => 'user:name'] |
| Count Column | ✅ | ['key' => 'items_count'] |
| JSON Column | ✅ | ['json' => 'field'] |
| Function Column | ❌ | ['function' => 'getStatus'] |
| Raw HTML | ❌ | ['raw' => '<button>'] |
| Nested Relation | ❌ | ['relation' => 'user.profile:name'] |
'filters' => [
'status' => ['type' => 'select'],
'price' => ['type' => 'number'],
'date' => ['type' => 'date'],
],
['key' => 'name', 'label' => 'Product Name']
['key' => 'category_name', 'label' => 'Category', 'relation' => 'category:name']
['key' => 'items_count', 'label' => 'Items']
['key' => 'data', 'label' => 'Color', 'json' => 'color']
['key' => 'price', 'label' => 'Price', 'raw' => '₹{{ $row->price }}']
['key' => 'status', 'label' => 'Status', 'function' => 'getStatus']
[@livewire](https://github.com/livewire)('aftable', [
// Required
'model' => 'App\Models\Item',
'columns' => [...],
// Sorting
'sortBy' => 'column_key',
'sortDirection' => 'asc|desc',
// Pagination
'records' => 50,
// Search
'searchable' => true,
// Filters
'filters' => [...],
// Export
'showExport' => true,
'exportFormats' => ['csv', 'excel', 'pdf'],
// UI
'colvisBtn' => true, // Column visibility toggle
'refreshBtn' => false, // Refresh button
'printable' => false, // Print button
'index' => true, // Row numbers
// Advanced
'customQuery' => Builder, // Custom Eloquent query
'data' => Collection, // Pre-loaded data
])
Use relation for related data
['key' => 'category_name', 'relation' => 'category:name']
Use count columns for aggregates
['key' => 'items_count'] // Auto-detected
Leverage sorting for fast data access
'sortBy' => 'created_at', 'sortDirection' => 'desc'
Set reasonable page size
'records' => 50 // Not 1000+
Enable search instead of scrolling
'searchable' => true
records to 1000+ by defaultProblem: Clicking headers doesn't sort
Solution: Ensure column is in database
// ✅ Works
['key' => 'email', 'label' => 'Email']
// ❌ Doesn't work (if not in DB)
['key' => 'full_name', 'label' => 'Full Name'] // Computed property
Problem: Relationship column is empty
Solution: Check relation format
// ❌ Wrong
['key' => 'category_id', 'relation' => 'category']
// ✅ Correct
['key' => 'category_id', 'relation' => 'category:name']
Problem: Search doesn't find data
Solution: Only text columns are searchable
// ✅ Searchable
['key' => 'name', 'label' => 'Name']
// ❌ Not searchable
['key' => 'price', 'label' => 'Price'] // Number field
Problem: Table takes >1 second to load
Solution: Check for N+1 queries
// ❌ Bad - 51 queries
'columns' => [['key' => 'user_name', 'label' => 'User']] // Manual fetch
// ✅ Good - 1 query
'columns' => [['key' => 'user_name', 'label' => 'User', 'relation' => 'user:name']]
docs/
├── SORTING_GUIDE.md (NEW!) Complete sorting guide
├── ENHANCED_FEATURES.md (NEW!) Advanced features
├── AI_USAGE_GUIDE.md Quick practical guide
├── USAGE_STUB.md All parameters
├── AI_TECHNICAL_REFERENCE.md Technical deep-dive
└── README.md This file
| Dataset | Items | Sorting | Search | Time | Queries |
|---|---|---|---|---|---|
| Small | 50 | ✅ | ✅ | 45ms | 1 |
| Medium | 500 | ✅ | ✅ | 85ms | 1 |
| Large | 5,000 | ✅ | ✅ | 150ms | 1 |
| XL | 50,000 | ✅ | ✅ | 250ms | 1 |
| Format | Rows | Time | Memory |
|---|---|---|---|
| CSV | 10K | 450ms | 8MB |
| Excel | 10K | 650ms | 12MB |
| 10K | 1.2s | 15MB |
sortBy parameter supportSORTING_GUIDE.md documentationENHANCED_FEATURES.md guideartflow-studio/table# Installation
composer require artflow-studio/table
# Update
composer update artflow-studio/table
# No additional setup needed!
# Component auto-registers with Laravel 5.5+
[@livewire](https://github.com/livewire)('aftable', [
'model' => 'App\Models\Item',
'columns' => [...],
'sortBy' => 'created_at',
'sortDirection' => 'desc',
])
'sortBy' => 'column_key' // Column to sort by
'sortDirection' => 'asc'|'desc' // Direction
'sort' => 'asc'|'desc' // Legacy (backward compat)
'searchable' => true // Enable search
'showExport' => true // Export button
'colvisBtn' => true // Column visibility
'refreshBtn' => false // Refresh button
'printable' => false // Print button
'index' => true // Row numbers
artflow-studio/tablesortBy for initial sortStatus: ✅ Production Ready
Last Updated: December 30, 2025
Version: 1.5.2+
How can I help you explore Laravel packages today?