Skip to main content

Overview

The BladeColumn allows you to render completely custom content in table cells using Blade views or raw HTML. It’s perfect for complex cell layouts, custom components, or any content that doesn’t fit the standard column types.

Creating a Blade Column

Basic Usage

use Livewire\Tables\Columns\BladeColumn;

BladeColumn::make()

Factory Method

You can also create blade columns using the base Column class:
use Livewire\Tables\Columns\Column;

Column::blade()

With Custom Field Name

BladeColumn::make('custom_content')
If no field name is provided, a unique field name is automatically generated (e.g., _blade_0, _blade_1).

Blade-Specific Methods

render
Closure
Define the content rendering logic. The closure receives the row model and optionally the table component. You can return a Blade view instance or raw HTML string.
// Return HTML string
BladeColumn::make()->render(fn($row) => '<strong>' . $row->name . '</strong>')

// Return Blade view
BladeColumn::make()->render(fn($row, $table) => view('components.user-badge', [
    'user' => $row,
    'table' => $table,
]))
searchable
Closure
Enable searching on blade columns by providing a custom search callback. Unlike other columns, blade columns require a closure to enable searching.
BladeColumn::make()
    ->render(fn($row) => view('cells.product', ['product' => $row]))
    ->searchable(fn($query, $search) => 
        $query->where('name', 'LIKE', "%{$search}%")
              ->orWhere('description', 'LIKE', "%{$search}%")
    )

Common Methods

label
string
Set a custom label for the column header.
BladeColumn::make()->label('Custom Content')
width
string
Set the column width.
BladeColumn::make()->width('200px')
columnClass
string
Apply CSS classes to both header and cells.
BladeColumn::make()->columnClass('text-center')
headerClass
string
Apply CSS classes only to the column header.
BladeColumn::make()->headerClass('text-center')
cellClass
string
Apply CSS classes only to the column cells.
BladeColumn::make()->cellClass('p-4')
hidden
void
Hide the column from display.
BladeColumn::make()->hidden()
hideIf
bool
Conditionally hide the column.
BladeColumn::make()->hideIf(!auth()->user()->isAdmin())
view
string
Specify a Blade view to render. This is a shortcut for using render() with a view.
BladeColumn::make()->view('components.status-badge')

Important Notes

  • Sorting: Blade columns cannot be made sortable (the sortable() method is ignored)
  • Searching: Basic searchable() without a closure does nothing. You must provide a closure for search functionality
  • Empty Render: If no render() callback is set, the column displays an empty string
  • resolveValue: Always returns null for blade columns

Examples

Basic HTML Rendering

public function columns(): array
{
    return [
        TextColumn::make('name')->sortable()->searchable(),
        
        BladeColumn::make()
            ->label('Badge')
            ->render(fn($row) => 
                '<span class="badge badge-' . $row->status . '">' . 
                ucfirst($row->status) . 
                '</span>'
            ),
    ];
}

Render Blade View

BladeColumn::make()
    ->label('User Profile')
    ->render(fn($row) => view('components.user-profile', ['user' => $row]))
{{-- resources/views/components/user-profile.blade.php --}}
<div class="flex items-center gap-3">
    <img src="{{ $user->avatar }}" class="w-10 h-10 rounded-full" alt="{{ $user->name }}">
    <div>
        <div class="font-semibold">{{ $user->name }}</div>
        <div class="text-sm text-gray-500">{{ $user->email }}</div>
    </div>
</div>

Complex Status Badge

BladeColumn::make()
    ->label('Status')
    ->render(function($row) {
        $statusConfig = [
            'active' => ['color' => 'green', 'icon' => '✓', 'text' => 'Active'],
            'pending' => ['color' => 'yellow', 'icon' => '⏱', 'text' => 'Pending'],
            'suspended' => ['color' => 'red', 'icon' => '✗', 'text' => 'Suspended'],
        ];
        
        $config = $statusConfig[$row->status] ?? ['color' => 'gray', 'icon' => '?', 'text' => 'Unknown'];
        
        return sprintf(
            '<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-%s-100 text-%s-800">'
            . '<span class="mr-1">%s</span>%s'
            . '</span>',
            $config['color'],
            $config['color'],
            $config['icon'],
            $config['text']
        );
    })

Multi-Line Cell Content

BladeColumn::make()
    ->label('Details')
    ->render(function($row) {
        return 
            '<div class="text-sm">' .
            '<div class="font-semibold">' . e($row->title) . '</div>' .
            '<div class="text-gray-500">' . e($row->subtitle) . '</div>' .
            '<div class="text-xs text-gray-400 mt-1">' . $row->created_at->diffForHumans() . '</div>' .
            '</div>';
    })

Progress Bar Column

BladeColumn::make()
    ->label('Progress')
    ->render(function($row) {
        $percentage = $row->completion_percentage ?? 0;
        $color = $percentage >= 100 ? 'green' : ($percentage >= 50 ? 'blue' : 'gray');
        
        return view('components.progress-bar', [
            'percentage' => $percentage,
            'color' => $color,
        ]);
    })
{{-- resources/views/components/progress-bar.blade.php --}}
<div class="w-full bg-gray-200 rounded-full h-2.5">
    <div class="bg-{{ $color }}-600 h-2.5 rounded-full" style="width: {{ $percentage }}%"></div>
</div>
<div class="text-xs text-gray-500 mt-1">{{ $percentage }}%</div>

Interactive Components

BladeColumn::make()
    ->label('Quick Actions')
    ->render(function($row, $table) {
        return view('livewire.table-cells.quick-actions', [
            'item' => $row,
            'tableComponent' => $table,
        ]);
    })
{{-- resources/views/livewire/table-cells/quick-actions.blade.php --}}
<div class="flex gap-2" x-data="{ showTooltip: false }">
    <button 
        wire:click="quickEdit({{ $item->id }})"
        @mouseenter="showTooltip = true"
        @mouseleave="showTooltip = false"
        class="p-1 hover:bg-gray-100 rounded">
        ✏️
    </button>
    
    <button 
        wire:click="toggleFavorite({{ $item->id }})"
        class="p-1 hover:bg-gray-100 rounded">
        {{ $item->is_favorite ? '★' : '☆' }}
    </button>
</div>

Tags Display

BladeColumn::make()
    ->label('Tags')
    ->render(function($row) {
        $tags = $row->tags ?? [];
        
        if (empty($tags)) {
            return '<span class="text-gray-400 text-sm">No tags</span>';
        }
        
        $html = '<div class="flex flex-wrap gap-1">';
        foreach ($tags as $tag) {
            $html .= '<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 text-blue-800">';
            $html .= e($tag->name);
            $html .= '</span>';
        }
        $html .= '</div>';
        
        return $html;
    })
    ->searchable(function($query, $search) {
        $query->whereHas('tags', fn($q) => 
            $q->where('name', 'LIKE', "%{$search}%")
        );
    })

Conditional Formatting with Icons

BladeColumn::make()
    ->label('Health')
    ->render(function($row) {
        $health = $row->health_score ?? 0;
        
        if ($health >= 80) {
            return '<div class="flex items-center gap-2 text-green-600">' .
                   '<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/></svg>' .
                   '<span>Healthy</span>' .
                   '</div>';
        } elseif ($health >= 50) {
            return '<div class="flex items-center gap-2 text-yellow-600">' .
                   '<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/></svg>' .
                   '<span>Warning</span>' .
                   '</div>';
        }
        
        return '<div class="flex items-center gap-2 text-red-600">' .
               '<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/></svg>' .
               '<span>Critical</span>' .
               '</div>';
    })
    ->columnClass('text-sm')

With Livewire Component

BladeColumn::make()
    ->label('Inline Editor')
    ->render(fn($row) => 
        '<livewire:inline-editor :model="$row" wire:key="editor-' . $row->id . '" />'
    )
BladeColumn::make()
    ->label('Comments')
    ->render(function($row) {
        $count = $row->comments_count ?? 0;
        
        if ($count === 0) {
            return '<span class="text-gray-400">No comments</span>';
        }
        
        return '<a href="' . route('posts.comments', $row->id) . '" class="inline-flex items-center gap-2 text-blue-600 hover:text-blue-800">' .
               '<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z"/></svg>' .
               '<span>' . $count . ' comment' . ($count !== 1 ? 's' : '') . '</span>' .
               '</a>';
    })

Rendering Logic

The BladeColumn uses a custom renderCell() method:
// Source code from BladeColumn.php:53-66
public function renderCell(Model $row, mixed $table = null): string
{
    if ($this->renderCallback === null) {
        return '';
    }

    $result = ($this->renderCallback)($row, $table);

    if ($result instanceof View) {
        return $result->render();
    }

    return (string) $result;
}
Key points:
  • Returns empty string if no render callback is set
  • Automatically renders Illuminate\Contracts\View\View instances
  • Casts other return values to string

Type

The column type identifier for BladeColumn is 'blade'.

Best Practices

  1. Keep it Simple: Blade columns are powerful, but complex logic can slow down table rendering
  2. Escape User Input: Always use e() or {{ }} in Blade to prevent XSS attacks
  3. Use View Components: For complex layouts, create reusable Blade components
  4. Avoid Heavy Queries: Don’t load relationships or make database queries inside render callbacks
  5. Consider Caching: If rendering is expensive, consider caching the output
  6. Accessibility: Include proper ARIA labels and semantic HTML

Build docs developers (and LLMs) love