Skip to main content
The Table component requires a Flux Pro license. Learn more about Flux Pro.

Overview

The Table component provides a powerful, feature-rich interface for displaying and managing tabular data. With built-in sorting, pagination, filtering, and row selection, it’s perfect for admin panels, dashboards, and data-heavy applications.

Basic Usage

Create a simple table:
<flux:table :rows="$users">
    <flux:table.column header="Name" :sortable="true">
        {{ $row->name }}
    </flux:table.column>
    
    <flux:table.column header="Email">
        {{ $row->email }}
    </flux:table.column>
    
    <flux:table.column header="Role">
        {{ $row->role }}
    </flux:table.column>
</flux:table>

With Sorting

Enable column sorting:
<flux:table :rows="$users" wire:sort="handleSort">
    <flux:table.column field="name" header="Name" :sortable="true" />
    <flux:table.column field="email" header="Email" :sortable="true" />
    <flux:table.column field="created_at" header="Joined" :sortable="true" />
</flux:table>
class UserTable extends Component
{
    public $sortField = 'name';
    public $sortDirection = 'asc';
    
    public function handleSort($field)
    {
        if ($this->sortField === $field) {
            $this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc';
        } else {
            $this->sortField = $field;
            $this->sortDirection = 'asc';
        }
    }
    
    public function getUsersProperty()
    {
        return User::orderBy($this->sortField, $this->sortDirection)->get();
    }
}

With Pagination

Add pagination controls:
<flux:table :rows="$users">
    <flux:table.column header="Name">{{ $row->name }}</flux:table.column>
    <flux:table.column header="Email">{{ $row->email }}</flux:table.column>
</flux:table>

<flux:pagination :paginator="$users" />
public function getUsersProperty()
{
    return User::paginate(15);
}

Row Selection

Enable selecting rows:
<flux:table :rows="$users" wire:model="selectedUsers" selectable>
    <flux:table.column header="Name">{{ $row->name }}</flux:table.column>
    <flux:table.column header="Email">{{ $row->email }}</flux:table.column>
</flux:table>

@if(count($selectedUsers) > 0)
    <div class="mt-4">
        <flux:button wire:click="deleteSelected">Delete {{ count($selectedUsers) }} users</flux:button>
    </div>
@endif

Row Actions

Add action buttons to rows:
<flux:table :rows="$users">
    <flux:table.column header="Name">{{ $row->name }}</flux:table.column>
    <flux:table.column header="Email">{{ $row->email }}</flux:table.column>
    
    <flux:table.column header="Actions">
        <div class="flex gap-2">
            <flux:button size="sm" wire:click="edit({{ $row->id }})">Edit</flux:button>
            <flux:button size="sm" variant="danger" wire:click="delete({{ $row->id }})">Delete</flux:button>
        </div>
    </flux:table.column>
</flux:table>

Custom Cell Rendering

Format cell content:
<flux:table :rows="$orders">
    <flux:table.column header="Order #">
        <a href="{{ route('orders.show', $row) }}" class="text-blue-600 hover:underline">
            #{{ $row->id }}
        </a>
    </flux:table.column>
    
    <flux:table.column header="Customer">
        <div class="flex items-center gap-2">
            <flux:avatar :src="$row->customer->avatar" size="sm" />
            <span>{{ $row->customer->name }}</span>
        </div>
    </flux:table.column>
    
    <flux:table.column header="Status">
        <flux:badge :color="$row->status_color">
            {{ $row->status }}
        </flux:badge>
    </flux:table.column>
    
    <flux:table.column header="Total">
        ${{ number_format($row->total, 2) }}
    </flux:table.column>
</flux:table>
Add search functionality:
<div class="space-y-4">
    <flux:input
        wire:model.live="search"
        placeholder="Search users..."
        icon="magnifying-glass"
    />
    
    <flux:table :rows="$users">
        <flux:table.column header="Name">{{ $row->name }}</flux:table.column>
        <flux:table.column header="Email">{{ $row->email }}</flux:table.column>
    </flux:table>
</div>
public $search = '';

public function getUsersProperty()
{
    return User::query()
        ->when($this->search, fn($query) => 
            $query->where('name', 'like', "%{$this->search}%")
                  ->orWhere('email', 'like', "%{$this->search}%")
        )
        ->paginate(15);
}

Empty State

Customize the empty state:
<flux:table :rows="$users">
    <flux:table.column header="Name">{{ $row->name }}</flux:table.column>
    <flux:table.column header="Email">{{ $row->email }}</flux:table.column>
    
    <x-slot name="empty">
        <div class="text-center py-12">
            <flux:icon name="users" class="w-12 h-12 mx-auto text-gray-400" />
            <h3 class="mt-4 text-lg font-medium">No users found</h3>
            <p class="text-gray-500 mt-2">Get started by creating a new user.</p>
            <flux:button class="mt-4" wire:click="createUser">Add User</flux:button>
        </div>
    </x-slot>
</flux:table>

Expandable Rows

Show additional details:
<flux:table :rows="$orders" expandable>
    <flux:table.column header="Order #">{{ $row->id }}</flux:table.column>
    <flux:table.column header="Customer">{{ $row->customer->name }}</flux:table.column>
    <flux:table.column header="Total">${{ $row->total }}</flux:table.column>
    
    <x-slot name="expansion" :row="$row">
        <div class="p-4 bg-gray-50">
            <h4 class="font-medium mb-2">Order Items</h4>
            <ul class="space-y-1">
                @foreach($row->items as $item)
                    <li>{{ $item->product->name }} - Qty: {{ $item->quantity }}</li>
                @endforeach
            </ul>
        </div>
    </x-slot>
</flux:table>

Use Cases

User Management

Display and manage users with sorting, filtering, and bulk actions.

Order Lists

Show customer orders with status badges and action buttons.

Data Reports

Present analytics and report data in a sortable, paginated format.

Inventory

Manage product inventory with search, filters, and stock indicators.

Features

Data Management

  • Column sorting (ascending/descending)
  • Pagination with customizable page sizes
  • Search and filtering
  • Bulk row selection

Customization

  • Custom cell rendering
  • Conditional row styling
  • Expandable row details
  • Custom empty states

Interactions

  • Row click handlers
  • Action buttons and menus
  • Inline editing
  • Keyboard navigation

Performance

  • Virtual scrolling for large datasets
  • Lazy loading
  • Efficient re-rendering
  • Debounced search

Advanced Example

Complete table with all features:
<div class="space-y-4">
    <div class="flex items-center justify-between gap-4">
        <flux:input
            wire:model.live.debounce.300ms="search"
            placeholder="Search..."
            icon="magnifying-glass"
            class="flex-1"
        />
        
        <flux:button wire:click="export">Export</flux:button>
    </div>
    
    @if(count($selected) > 0)
        <div class="bg-blue-50 p-4 rounded-lg flex items-center justify-between">
            <span>{{ count($selected) }} items selected</span>
            <div class="flex gap-2">
                <flux:button size="sm" wire:click="bulkEdit">Edit</flux:button>
                <flux:button size="sm" variant="danger" wire:click="bulkDelete">Delete</flux:button>
            </div>
        </div>
    @endif
    
    <flux:table
        :rows="$users"
        wire:model="selected"
        wire:sort="handleSort"
        selectable
    >
        <flux:table.column field="name" header="Name" :sortable="true">
            <div class="flex items-center gap-2">
                <flux:avatar :src="$row->avatar" size="sm" />
                <div>
                    <div class="font-medium">{{ $row->name }}</div>
                    <div class="text-sm text-gray-500">{{ $row->email }}</div>
                </div>
            </div>
        </flux:table.column>
        
        <flux:table.column field="role" header="Role" :sortable="true">
            <flux:badge>{{ $row->role }}</flux:badge>
        </flux:table.column>
        
        <flux:table.column field="status" header="Status" :sortable="true">
            <flux:badge :color="$row->status === 'active' ? 'green' : 'gray'">
                {{ $row->status }}
            </flux:badge>
        </flux:table.column>
        
        <flux:table.column field="created_at" header="Joined" :sortable="true">
            {{ $row->created_at->format('M d, Y') }}
        </flux:table.column>
        
        <flux:table.column header="">
            <flux:popover placement="bottom-end">
                <flux:popover.trigger>
                    <flux:button icon="ellipsis-vertical" size="sm" variant="ghost" />
                </flux:popover.trigger>
                <flux:popover.content>
                    <flux:menu>
                        <flux:menu.item wire:click="view({{ $row->id }})">View</flux:menu.item>
                        <flux:menu.item wire:click="edit({{ $row->id }})">Edit</flux:menu.item>
                        <flux:menu.separator />
                        <flux:menu.item wire:click="delete({{ $row->id }})" variant="danger">
                            Delete
                        </flux:menu.item>
                    </flux:menu>
                </flux:popover.content>
            </flux:popover>
        </flux:table.column>
        
        <x-slot name="empty">
            <div class="text-center py-12">
                <p class="text-gray-500">No users found</p>
            </div>
        </x-slot>
    </flux:table>
    
    <flux:pagination :paginator="$users" />
</div>
For better performance with large datasets, use cursor pagination instead of offset pagination and implement virtual scrolling for tables with hundreds of rows.

Build docs developers (and LLMs) love