A flexible table component for displaying structured data with support for sorting and pagination.
A simple table with columns, rows, and cells. Use variant="strong" on a cell to bold the primary column.
| Name | Role | Status | |
|---|---|---|---|
| Alice Johnson | alice@example.com | Admin | Active |
| Bob Martinez | bob@example.com | Editor | Active |
| Carol White | carol@example.com | Viewer | Inactive |
| David Lee | david@example.com | Editor | Active |
<x-ui::table>
<x-ui::table-columns>
<x-ui::table-column>Name</x-ui::table-column>
<x-ui::table-column>Email</x-ui::table-column>
<x-ui::table-column>Role</x-ui::table-column>
<x-ui::table-column align="end">Status</x-ui::table-column>
</x-ui::table-columns>
<x-ui::table-rows>
@foreach($users as $user)
<x-ui::table-row>
<x-ui::table-cell variant="strong">{{ $user->name }}</x-ui::table-cell>
<x-ui::table-cell>{{ $user->email }}</x-ui::table-cell>
<x-ui::table-cell>{{ $user->role }}</x-ui::table-cell>
<x-ui::table-cell align="end">{{ $user->status }}</x-ui::table-cell>
</x-ui::table-row>
@endforeach
</x-ui::table-rows>
</x-ui::table>
Add sortable, :sorted, and :direction to a column to show sort indicators. Wire up a Livewire sort() action to handle ordering.
// Livewire component
public string $sortBy = 'name';
public string $sortDirection = 'asc';
public function sort(string $column): void
{
if ($this->sortBy === $column) {
$this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc';
} else {
$this->sortBy = $column;
$this->sortDirection = 'asc';
}
}
// Template
<x-ui::table>
<x-ui::table-columns>
<x-ui::table-column
sortable
:sorted="$sortBy === 'name'"
:direction="$sortDirection"
wire:click="sort('name')"
href="#"
>
Name
</x-ui::table-column>
</x-ui::table-columns>
...
</x-ui::table>
Add selectable to x-ui::table and x-ui::table-columns, then pass :value to each x-ui::table-row. A "select all" header checkbox and per-row checkboxes are rendered automatically.
| Name | Role | Status | ||
|---|---|---|---|---|
| Alice Johnson | alice@example.com | Admin | Active | |
| Bob Martinez | bob@example.com | Editor | Active | |
| Carol White | carol@example.com | Viewer | Inactive | |
| David Lee | david@example.com | Editor | Active | |
| Eva Brown | eva@example.com | Viewer | Pending | |
| Frank Garcia | frank@example.com | Admin | Active | |
| Grace Kim | grace@example.com | Editor | Inactive |
{{-- Blade template --}}
<x-ui::table selectable>
<x-ui::table-columns selectable>
<x-ui::table-column>Name</x-ui::table-column>
<x-ui::table-column>Email</x-ui::table-column>
<x-ui::table-column align="end">Status</x-ui::table-column>
</x-ui::table-columns>
<x-ui::table-rows>
@@foreach($users as $user)
<x-ui::table-row :value="$user->id">
<x-ui::table-cell variant="strong">{{ $user->name }}</x-ui::table-cell>
<x-ui::table-cell>{{ $user->email }}</x-ui::table-cell>
<x-ui::table-cell align="end">{{ $user->status }}</x-ui::table-cell>
</x-ui::table-row>
@@endforeach
</x-ui::table-rows>
</x-ui::table>
{{-- To sync selected IDs with Livewire, pass an @change listener: --}}
<x-ui::table selectable @change.capture="$wire.selected = selected">
...
</x-ui::table>
The selected array lives in Alpine state on the table wrapper. Use @change.capture="$wire.selected = selected" on the table element to sync it with a Livewire public array $selected = [] property.
Clicking a selected row's checkbox deselects it. The "select all" checkbox shows an indeterminate state when only some rows are checked. Clicking it selects all; clicking again clears all.
Pass a LengthAwarePaginator to :paginate and the table automatically renders the custom x-ui::pagination component below — with page controls and a "Showing X–Y of Z" summary.
| Name | Role | Status | |
|---|---|---|---|
| David Lee | david@example.com | Editor | Active |
| Eva Brown | eva@example.com | Viewer | Pending |
| Frank Garcia | frank@example.com | Admin | Active |
// Livewire component
use Illuminate\Pagination\LengthAwarePaginator;
use Livewire\WithPagination;
use WithPagination;
public int $perPage = 10;
#[Computed]
public function users(): LengthAwarePaginator
{
$all = User::query()->get();
$page = $this->getPage();
$items = $all->slice(($page - 1) * $this->perPage, $this->perPage)->values();
return new LengthAwarePaginator($items, $all->count(), $this->perPage, $page, [
'path' => request()->url(),
]);
}
// Template — the table renders <x-ui::pagination> automatically
<x-ui::table :paginate="$this->users">
...
<x-ui::table-rows>
@foreach($this->users->items() as $user)
<x-ui::table-row>...</x-ui::table-row>
@endforeach
</x-ui::table-rows>
</x-ui::table>
Use pagination-size to match the pagination controls to the density of your table. Default is md.
Small
| Name | Role | Status |
|---|---|---|
| David Lee | Editor | Active |
| Eva Brown | Viewer | Pending |
| Frank Garcia | Admin | Active |
Large
| Name | Role | Status |
|---|---|---|
| David Lee | Editor | Active |
| Eva Brown | Viewer | Pending |
| Frank Garcia | Admin | Active |
{{-- Small pagination controls --}}
<x-ui::table :paginate="$this->users" pagination-size="sm">
...
</x-ui::table>
{{-- Medium (default) --}}
<x-ui::table :paginate="$this->users" pagination-size="md">
...
</x-ui::table>
{{-- Large --}}
<x-ui::table :paginate="$this->users" pagination-size="lg">
...
</x-ui::table>
Set :show-info="false" to hide the "Showing X–Y of Z results" summary and render only the page controls, centered below the table.
| Name | Role | Status |
|---|---|---|
| David Lee | Editor | Active |
| Eva Brown | Viewer | Pending |
| Frank Garcia | Admin | Active |
{{-- Hide "Showing X–Y of Z results" --}}
<x-ui::table :paginate="$this->users" :show-info="false">
...
</x-ui::table>