Skip to main content

Overview

Columns define which fields from your model are displayed in the table. All columns extend the base Column class and implement ColumnContract.

Defining Columns

Override the columns() method in your table component:
use Livewire\Tables\Columns\TextColumn;
use Livewire\Tables\Columns\DateColumn;
use Livewire\Tables\Columns\BooleanColumn;

public function columns(): array
{
    return [
        TextColumn::make('name')->sortable()->searchable(),
        TextColumn::make('email')->sortable()->searchable(),
        BooleanColumn::make('active')->sortable(),
        DateColumn::make('created_at')->sortable(),
    ];
}

Column Types

TextColumn

Display plain text values:
TextColumn::make('name')
    ->label('Product Name')
    ->sortable()
    ->searchable();

Formatting

Transform the displayed value:
TextColumn::make('price')
    ->label('Price')
    ->sortable()
    ->format(fn($value) => '$' . number_format($value, 2));

Computed Columns

Create columns without a database field using render():
TextColumn::make()
    ->label('Full Name')
    ->render(fn($row) => $row->first_name . ' ' . $row->last_name);
When using make() with no arguments, you must provide a render() callback to define the cell content.

BooleanColumn

Display true/false values with visual indicators:
BooleanColumn::make('active')
    ->label('Status')
    ->sortable();
Renders a badge automatically:
  • True: Green badge
  • False: Red badge

Custom Labels

BooleanColumn::make('active')
    ->labels('Active', 'Inactive');

Custom Rendering

BooleanColumn::make('verified')
    ->render(fn($row) => $row->verified ? '✅' : '❌');

BooleanColumn::make('active')
    ->format(fn($value) => $value ? 'Enabled' : 'Disabled');
Priority order: render() > format() > default badge labels.

DateColumn

Display date values with customizable formatting:
DateColumn::make('created_at')
    ->label('Created')
    ->sortable()
    ->format('M d, Y');

Format Options

DateColumn::make('created_at')
    ->format('Y-m-d');

DateColumn::make('created_at')
    ->format('d/m/Y H:i');
Handles DateTimeInterface, strings, and null values automatically.

ImageColumn

Display images with lightbox preview:
ImageColumn::make('avatar_url')
    ->label('Photo')
    ->alt('name')
    ->dimensions(48, 48)
    ->width('64px');
MethodDescription
alt(string $field)Model field used for the alt attribute
dimensions(int $w, int $h)Image display dimensions
width(string $css)Column CSS width (e.g., '200px')
Click to open a lightbox preview (Alpine.js x-teleport).

BladeColumn

Render custom Blade views or HTML:
BladeColumn::make()
    ->label('Status')
    ->render(fn($row, $table) => view('tables.user-status', [
        'user' => $row,
        'table' => $table,
    ]));
The closure receives:
  • $row — The Eloquent model for the current row
  • $table — The Livewire DataTableComponent instance

Searchable BladeColumn

Use a closure to define custom search logic:
BladeColumn::make()
    ->label('Full Name')
    ->searchable(fn(Builder $query, string $search) => $query
        ->orWhere('first_name', 'LIKE', "%{$search}%")
        ->orWhere('last_name', 'LIKE', "%{$search}%")
    )
    ->render(fn($row) => $row->first_name . ' ' . $row->last_name);

ActionColumn

Row action buttons for edit, delete, or custom operations:
ActionColumn::make()
    ->label('Actions')
    ->button(
        label: 'Edit',
        action: fn($row) => "editItem({$row->id})",
        class: 'lt-btn-primary',
    )
    ->button(
        label: 'Delete',
        action: fn($row) => "deleteItem({$row->id})",
        class: 'lt-btn-primary',
        visible: fn($row) => $row->can_delete,
    );

Button Parameters

ParameterTypeDescription
labelstringButton text
actionClosureReturns the wire:click expression. Receives ($row, $table)
classstringCSS classes for the button
iconstring|nullOptional SVG/HTML icon prepended to label
visibleClosure|boolPer-row visibility. Closure receives ($row)

Complete Example

public function columns(): array
{
    return [
        TextColumn::make('name')->sortable()->searchable(),
        TextColumn::make('email')->sortable(),
        ActionColumn::make()
            ->label('Actions')
            ->button(
                label: 'View',
                action: fn($row) => "viewUser({$row->id})",
                class: 'lt-btn-primary',
                icon: '<svg class="w-4 h-4">...</svg>',
            )
            ->button(
                label: 'Delete',
                action: fn($row) => "confirmDelete({$row->id})",
                class: 'lt-btn-primary',
                visible: fn($row) => auth()->user()->can('delete', $row),
            ),
    ];
}

public function viewUser(int $id): void
{
    $this->redirect(route('users.show', $id));
}

public function confirmDelete(int $id): void
{
    User::findOrFail($id)->delete();
}

Common Methods

All column types share these methods:

label()

Set the column header text:
TextColumn::make('name')->label('Full Name');

sortable()

Enable sorting on this column:
TextColumn::make('name')->sortable();
Users can click the column header to sort. Multi-column sorting is supported by holding Shift.

searchable()

Include this column in global search:
TextColumn::make('name')->searchable();

Search on Specific Field

Useful for joined columns:
TextColumn::make('brand_name')
    ->label('Brand')
    ->sortable()
    ->searchable('brands.name');

Custom Search Logic

For BladeColumn or complex search:
BladeColumn::make()
    ->searchable(fn(Builder $query, string $search) => $query
        ->orWhere('first_name', 'LIKE', "%{$search}%")
        ->orWhere('last_name', 'LIKE', "%{$search}%")
    )
    ->render(fn($row) => $row->first_name . ' ' . $row->last_name);

format()

Transform the displayed value:
TextColumn::make('price')
    ->format(fn($value) => '$' . number_format($value, 2));

TextColumn::make('status')
    ->format(fn($value, $row) => strtoupper($value));

render()

Compute cell content from the row model:
TextColumn::make()
    ->label('Total')
    ->render(fn($row) => $row->quantity * $row->unit_price);
render() takes precedence over format(). If both are set, format() is ignored.

hidden() / hideIf()

Hide columns by default:
TextColumn::make('internal_notes')->hidden();

TextColumn::make('admin_notes')->hideIf(! auth()->user()->isAdmin());

width()

Set column CSS width:
TextColumn::make('sku')->width('120px');

Styling Methods

TextColumn::make('price')
    ->headerClass('text-right bg-green-50')
    ->cellClass('text-right font-semibold');

TextColumn::make('id')
    ->columnClass('col-id'); // Applied to both <th> and <td>

key()

Override the internal key:
TextColumn::make('name')->key('product_name');

selectAs()

Override the resolution alias for joined columns:
TextColumn::make('brands.name')
    ->selectAs('brand_name')
    ->sortable();

view()

Use a custom Blade view for rendering:
TextColumn::make('status')->view('tables.status-badge');

Factory Shorthands

Use the Column factory for cleaner syntax:
use Livewire\Tables\Columns\Column;

Column::text('name');
Column::boolean('active');
Column::date('created_at');
Column::image('avatar');
Column::blade();
Column::actions();

Joined Columns

For columns from joined tables, use dot notation:
public function query(): Builder
{
    return Order::query()
        ->join('brands', 'orders.brand_id', '=', 'brands.id')
        ->select(
            'orders.*',
            'brands.name as brands_name',
            'brands.country as brands_country'
        );
}

public function columns(): array
{
    return [
        TextColumn::make('orders.id')->label('#')->sortable(),
        TextColumn::make('customer_name')->sortable()->searchable(),
        TextColumn::make('brands.name')->label('Brand')->sortable()->searchable(),
        TextColumn::make('brands.country')->label('Country')->sortable(),
    ];
}
When using joins, always select table.* to avoid column conflicts. Use aliases for ambiguous columns.

Searching on Aliases

If you use an alias in the SELECT, specify the actual database column for search:
TextColumn::make('brand_name')
    ->label('Brand')
    ->sortable()
    ->searchable('brands.name');

Complete Example

app/Livewire/ProductsTable.php
<?php

namespace App\Livewire;

use App\Models\Product;
use Illuminate\Database\Eloquent\Builder;
use Livewire\Tables\Columns\Column;
use Livewire\Tables\Livewire\DataTableComponent;

class ProductsTable extends DataTableComponent
{
    public function query(): Builder
    {
        return Product::query();
    }

    public function columns(): array
    {
        return [
            Column::image('image_url')
                ->label('Image')
                ->dimensions(80, 80)
                ->alt('name'),

            Column::text('name')
                ->label('Product')
                ->sortable()
                ->searchable(),

            Column::text('sku')
                ->label('SKU')
                ->sortable()
                ->searchable(),

            Column::text('price')
                ->label('Price')
                ->sortable()
                ->headerClass('text-right bg-green-50')
                ->cellClass('text-right font-semibold')
                ->format(fn($value) => '$' . number_format($value, 2)),

            Column::text('stock')
                ->label('Stock')
                ->sortable()
                ->headerClass('text-right')
                ->cellClass('text-right'),

            Column::boolean('active')
                ->label('Status')
                ->sortable()
                ->labels('Active', 'Inactive'),

            Column::date('release_date')
                ->label('Released')
                ->sortable()
                ->format('M d, Y'),

            Column::actions()
                ->button('Edit', fn($row) => "edit({$row->id})", 'lt-btn-primary')
                ->button('Delete', fn($row) => "delete({$row->id})", 'lt-btn-primary'),
        ];
    }
}

Next Steps

Filters

Add filters to narrow down your data

Sorting & Search

Configure search and multi-column sorting

Build docs developers (and LLMs) love