Table

Simple

# Nice Name City
1 Gladyce New Bryce
2 Taurean Wunschhaven
3 Malvina North Jeradside
4 Guillermo West Carrie
5 Jeramie Torphymouth
@php
$users = App\Models\User::with('city')->take(5)->get();
 
$headers = [
['key' => 'id', 'label' => '#'],
['key' => 'name', 'label' => 'Nice Name'],
['key' => 'city.name', 'label' => 'City'] # <---- nested attributes
];
@endphp
 
{{-- You can use any `$wire.METHOD` on `@row-click` --}}
<x-table :headers="$headers" :rows="$users" striped @row-click="alert($event.detail.name)" />

No headers

Gladyce New Bryce
Taurean Wunschhaven
@php
$users = App\Models\User::with('city')->take(2)->get();
 
$headers = [
['key' => 'name', 'label' => 'Name'],
['key' => 'city.name', 'label' => 'City'],
];
@endphp
 
{{-- Notice `no-headers` --}}
<x-table :headers="$headers" :rows="$users" no-headers />

Click to navigate

The following {tokens} will be replaced by actual values on each row based on any key from $headers config object.

Some examples:

  • /users/{id}
  • /users/profile/{username}/?&admin=true
  • /users/{id}/?from={city.name}

@php
$users = App\Models\User::with('city')->take(2)->get();
 
$headers = [
['key' => 'id', 'label' => '#'],
['key' => 'username', 'label' => 'Username'],
['key' => 'city.name', 'label' => 'City'],
];
@endphp
 
{{-- Notice `link` --}}
{{-- Check browser url on next page --}}
<x-table :headers="$headers" :rows="$users" link="/docs/installation/?from={username}" />

Header classes

Any class set on $headers will be applied to respective columns. You can also control columns visibility using Tailwind responsive breakpoints. Resize this window to see it.

# Username
1 gutmann.kadin
2 vadams
3 nicolas.franco
@php
use App\Models\User;
$users = User::take(3)->get();
 
$headers = [
['key' => 'id', 'label' => '#', 'class' => 'bg-red-500/20 w-1'],
['key' => 'username', 'label' => 'Username'],
['key' => 'email', 'label' => 'E-mail', 'class' => 'hidden lg:table-cell'],
];
@endphp
 
<x-table :headers="$headers" :rows="$users" />

Row and cell decoration

It is possible to define custom logic to apply background colors, or any class, on rows and/or cells. Remember to configure Tailwind safelist to compile dynamic css classes you are using.

Nice Name Username
Gladyce gutmann.kadin
Taurean vadams
Malvina nicolas.franco
@php
use App\Models\User;
$users = User::take(3)->get();
 
// Considering this scenario
$users[0]->isAdmin = true;
$users[0]->isInactive = true;
 
$users[1]->isAdmin = true;
$users[1]->isInactive = false;
 
$users[2]->isAdmin = false;
$users[2]->isInactive = true;
 
$headers = [
['key' => 'name', 'label' => 'Nice Name'],
['key' => 'username', 'label' => 'Username'],
];
 
// The `item` will be injected on current loop context.
// You can apply any logic to define what class will be applied.
// If more than one condition is `true` the respective classes will be merged.
 
$row_decoration = [
'bg-yellow-500/25' => fn(User $user) => $user->isAdmin,
'text-red-500' => fn(User $user) => $user->isAdmin && $user->isInactive,
'underline font-bold' => fn(User $user) => $user->isInactive // <-- combined classes
];
@endphp
 
<x-table :headers="$headers" :rows="$users" :row-decoration="$row_decoration" />

You can do the same for cells.

Nice Name Username City
Gladyce gutmann.kadin New Bryce
Taurean vadams Wunschhaven
Malvina nicolas.franco North Jeradside
@php
use App\Models\User;
$users = User::take(3)->get();
 
// Considering this scenario
$users[0]->isAdmin = true;
$users[0]->isInactive = true;
$users[0]['city']->isAvailable = true;
$users[1]['city']->isAvailable = false;
 
 
$headers = [
['key' => 'name', 'label' => 'Nice Name'],
['key' => 'username', 'label' => 'Username'],
['key' => 'city.name', 'label' => 'City'],
];
 
// Use the same `headers key`.
// The `item` will be injected on current loop context.
// You can apply any logic to define what class will be applied.
// If more than one condition is `true` the respective classes will be merged.
 
$cell_decoration = [
'city.name' => [
'bg-yellow-500/25 underline' => fn(User $user) => !$user->city->isAvailable,
],
'username' => [
'text-yellow-500' => fn(User $user) => $user->isAdmin,
'bg-dark-300' => fn(User $user) => $user->isInactive
]
];
@endphp
 
<x-table :headers="$headers" :rows="$users" :cell-decoration="$cell_decoration" />

Sort

Declare a property $sortBy within following pattern bellow. It will be updated when you click on table headers. So, you can use it to order your query.

public array $sortBy = ['column' => 'name', 'direction' => 'asc'];
 
public function users(): Collection
{
return User::query()
->orderBy(...array_values($this->sortBy))
->take(3)
->get();
}

By default, all columns will be sortable. Check the following example to disable sorting on specific columns.

# Name E-mail
30 Aaliyah lilla.carter@example.net
94 Abbey oullrich@example.net
73 Abbigail haylee30@example.org
@php
$users = $this->users();
$sortBy = $this->sortBy;
$headers = [
['key' => 'id', 'label' => '#', 'class' => 'w-16'],
['key' => 'name', 'label' => 'Name', 'class' => 'w-72'],
['key' => 'email', 'label' => 'E-mail', 'sortable' => false], // <--- Won't be sortable
];
@endphp
 
{{-- Notice `sort-by` --}}
<x-table :headers="$headers" :rows="$users" :sort-by="$sortBy" />

If you plan to sort on relationship fields, consider using withAggregate() Eloquent method. It will add an extra column on result.

// It will add an extra column `city_name` on User collection
User::withAggregate('city', 'name')-> ...
# Name City
30 Aaliyah South Matildaside
94 Abbey West Sydneyland
73 Abbigail South Fernefort
@php
$users = $this->users();
$sortBy = $this->sortBy;
$headers = [
['key' => 'id', 'label' => '#', 'class' => 'w-16'],
['key' => 'name', 'label' => 'Name', 'class' => 'w-72'],
['key' => 'city_name', 'label' => 'City'], // <--- Notice 'city_name' syntax
];
@endphp
 
{{-- Notice `sort-by` --}}
<x-table :headers="$headers" :rows="$users" :sort-by="$sortBy" />

In the following example we need City as complex object to use it in a custom slot. But, it must be sorted by a custom column. So we make use of same approach of withAggregate combined with a sortBy per column.

// It will add an extra column `city_name` on User collection
User::withAggregate('city', 'name')-> ...
# Name City
30 Aaliyah
South Matildaside
94 Abbey
West Sydneyland
73 Abbigail
South Fernefort
@php
$users = $this->users();
$sortBy = $this->sortBy;
$headers = [
['key' => 'id', 'label' => '#', 'class' => 'w-16'],
['key' => 'name', 'label' => 'Name', 'class' => 'w-72'],
['key' => 'city', 'label' => 'City', 'sortBy' => 'city_name'], // <--- Notice 'sortBy'
];
@endphp
 
{{-- You will learn about custom slots on next sections --}}
<x-table :headers="$headers" :rows="$users" :sort-by="$sortBy">
@scope('cell_city', $user)
<x-badge :value="$user->city->name" class="badge-primary" />
@endscope
</x-table>

Pagination

Notice maryUI uses directly all features offered by Laravel and Livewire itself, including default pagination links and deeper customizations. For further details, please, refer to their docs.

As described on Laravel docs you need to adjust your tailwind.config.js

content: [
// Add this
'./vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php',
],

Then, use WithPagination trait from Livewire itself, as described on Livewire docs.

use Livewire\WithPagination;
 
class ShowUsers extends Component
{
use WithPagination;
}

# Nice Name
1 Gladyce
2 Taurean
3 Malvina
@php
$users = App\Models\User::paginate(3);
 
$headers = [
['key' => 'id', 'label' => '#', 'class' => 'w-1'],
['key' => 'name', 'label' => 'Nice Name'],
];
@endphp
 
{{-- Notice `with-pagination` --}}
<x-table :headers="$headers" :rows="$users" with-pagination />

Here are some useful styles to add on app.css. Notice this is about default classes provided by Laravel paginator links not maryUI itself. Fell free to change it.

/* Active page highlight */
.mary-table-pagination span[aria-current="page"] > span {
@apply bg-primary text-base-100
}
 
/* For dark mode*/
.mary-table-pagination span[aria-disabled="true"] span {
@apply bg-inherit
}
 
/* For dark mode*/
.mary-table-pagination button {
@apply bg-base-100
}

Header slot

You can override any header by using @scope('header_XXX', $header) special blade directive, in which XXX is any key from $headers config object.


# Nice Name City
1 Gladyce New Bryce
2 Taurean Wunschhaven
@php
$users = App\Models\User::with('city')->take(2)->get();
 
$headers = [
['key' => 'id', 'label' => '#', 'class' => 'bg-red-500/20'], # <--- custom CSS
['key' => 'name', 'label' => 'Nice Name'],
['key' => 'city.name', 'label' => 'City'], # <---- nested attributes
];
@endphp
 
<x-table :headers="$headers" :rows="$users">
{{-- Overrides `name` header --}}
@scope('header_name', $header)
{{ $header['label'] }} <x-icon name="s-question-mark-circle" />
@endscope
 
{{-- Overrides `city.name` header --}}
@scope('header_city.name', $header)
<u>{{ $header['label'] }}</u>
@endscope
</x-table>

Cell slot

You can override any row by using @scope('cell_XXX', $row) special blade directive, in which XXX is any key from $headers config object.

It injects current $row from the loop's context and achieves the same behavior that you would expect from the Vue/React components.

Notice that you do not need to override all the attributes.


# Nice Name City Fake City
1
Gladyce
New Bryce New Bryce
2
Taurean
Wunschhaven Wunschhaven
3
Malvina
North Jeradside North Jeradside
@php
$users = App\Models\User::with('city')->take(3)->get();
 
$headers = [
['key' => 'id', 'label' => '#'],
['key' => 'name', 'label' => 'Nice Name'],
['key' => 'city.name', 'label' => 'City'], # <-- nested attributes
['key' => 'fakeColumn', 'label' => 'Fake City'] # <-- this column does not exists
];
@endphp
 
<x-table :headers="$headers" :rows="$users">
 
{{-- Notice `$user` is the current row item on loop --}}
@scope('cell_id', $user)
<strong>{{ $user->id }}</strong>
@endscope
 
{{-- You can name the injected object as you wish --}}
@scope('cell_name', $stuff)
<x-badge :value="$stuff->name" class="badge-info" />
@endscope
 
{{-- Notice the `dot` notation for nested attribute cell's slot --}}
@scope('cell_city.name', $user)
<i>{{ $user->city->name }}</i>
@endscope
 
{{-- The `fakeColumn` does not exist to the actual object --}}
@scope('cell_fakeColumn', $user)
<u>{{ $user->city->name }}</u>
@endscope
 
{{-- Special `actions` slot --}}
@scope('actions', $user)
<x-button icon="o-trash" wire:click="delete({{ $user->id }})" spinner class="btn-sm" />
@endscope
 
</x-table>

Inject external variables

You can inject any external variables into any cell scope like this.

Name
Gladyce - 1 - 2
Taurean - 1 - 2
Malvina - 1 - 2
@php
$users = App\Models\User::with('city')->take(3)->get();
$headers = [['key' => 'name', 'label' => 'Name']];
$some = 1;
$thing = 2;
@endphp
 
<x-table :headers="$headers" :rows="$users">
{{-- The `$user` viariable is the injected automatically from the current loop context --}}
{{-- You can pass any extra arbitrary variables after that --}}
@scope('cell_name', $user, $some, $thing)
{{ $user->name }} - {{ $some }} - {{ $thing }}
@endscope
</x-table>

Loop context

You can access the loop context on cell scopes through $loop variable.

Nice Name
(0) Gladyce
(1) Taurean
(2) Malvina
@php
$users = App\Models\User::with('city')->take(3)->get();
 
$headers = [
['key' => 'name', 'label' => 'Nice Name'],
];
@endphp
 
<x-table :headers="$headers" :rows="$users">
@scope('cell_name', $user)
({{ $loop->index }}) {{ $user->name }}
@endscope
</x-table>

Row selection

Use selectable attribute in conjunction with wire:model to manage selection state.

public array $selected = [1, 3];

# Nice Name
1 Gladyce
2 Taurean
3 Malvina
@php
$users = App\Models\User::take(3)->get();
 
$headers = [
['key' => 'id', 'label' => '#', 'class' => 'w-1'],
['key' => 'name', 'label' => 'Nice Name'],
];
@endphp
 
{{-- Notice `selectable` and `wire:model` --}}
{{-- See `@row-selection` output on console --}}
{{-- You can use any `$wire.METHOD` on `@row-selection` --}}
<x-table
:headers="$headers"
:rows="$users"
wire:model="selected"
selectable
@row-selection="console.log($event.detail)" />
 
<x-button label="Save" icon="o-check" wire:click="save" spinner />

By default, it will look up for $row->id attribute. You can customize this with selectable-key attribute.

{{-- Uses `$row->mycode` as selection key --}}
<x-table ... selectable selectable-key="mycode" />

Row expansion

Use expandable attribute in conjunction with wire:model to manage expansion state.

public array $expanded = [2];

Nice Name
Gladyce
Hello, Gladyce!
Taurean
Hello, Taurean!
Malvina
Hello, Malvina!
@php
$users = App\Models\User::take(3)->get();
 
$headers = [
['key' => 'id', 'label' => '#', 'class' => 'hidden'],
['key' => 'name', 'label' => 'Nice Name'],
];
@endphp
 
{{-- Notice `expandable` and `wire:model` --}}
<x-table :headers="$headers" :rows="$users" wire:model="expanded" expandable>
 
{{-- Special `expansion` slot --}}
@scope('expansion', $user)
<div class="bg-base-200 p-8 font-bold">
Hello, {{ $user->name }}!
</div>
@endscope
 
</x-table>

By default, it will look up for $row->id attribute. You can customize this with expandable-key attribute.

{{-- Uses `$row->mycode` as expandable key --}}
<x-table ... expandable expandable-key="mycode" />

maryUI
Sponsor