K

Table

Columns

Wire Table provides 12 column types. They all share the same base column API for labels, visibility, authorization, sorting, formatting, and inline editing — documented below. Pick a type for its cell rendering; reach for the shared API on any of them.

On this page

Wire Table provides 12 column types. They all share the same base column API for labels, visibility, authorization, sorting, formatting, and inline editing — documented below. Pick a type for its cell rendering; reach for the shared API on any of them.

Column Types

Column Use for
TextColumn General-purpose text with date/money/number formatting presets
BadgeColumn Status pills with color and icon, incl. enum self-coloring
BooleanColumn True/false as an icon (check / cross)
IconColumn State-based or dynamically resolved icons
ImageColumn Avatars and thumbnails
ButtonColumn Link or Livewire-action button in a cell
ToggleColumn Inline editable on/off switch
SelectColumn Inline editable dropdown (options, relations, enums)
TextInputColumn Inline editable text/number/email input
StackedColumn Avatar + name + email stacked layouts
SplitColumn Compose several columns side by side
PollColumn Live-polling status/progress cells

Concepts

Shared Column API

Every column inherits these capabilities from the base Column class.

Factory & Identity

Column::make(string $name) // static factory — $name is dot-notation path
->label(string|Closure $label) // display label in <th> (auto-generated from name)
->getName(): string // get column name
->getLabel(): string // get resolved label

Sorting

->sortable(bool $sortable = true, ?Closure $query = null)
->isSortable(): bool
 
// Custom sort logic
->sortUsing(Closure $fn)
TextColumn::make('full_name')
->sortable()
->sortUsing(function (Builder $query, string $direction) {
$query->orderBy('last_name', $direction)
->orderBy('first_name', $direction);
})

Searching

->searchable(bool|array $searchable = true)
->isSearchable(): bool
 
// Pass an array to search specific DB columns (when the column name is virtual)
->searchable(['first_name', 'last_name', 'email'])
 
// Custom search logic
->searchUsing(Closure $fn)
 
// Get resolved search columns
->getSearchColumns(): array

searchColumns(array $columns) as a separate setter exists only on StackedColumn. On other columns, pass the array straight to searchable().

// Search across multiple DB columns
TextColumn::make('user')
->searchable(['first_name', 'last_name', 'email'])
 
// Custom search logic
TextColumn::make('full_name')
->searchable()
->searchUsing(function (Builder $query, string $search) {
$query->where(DB::raw("CONCAT(first_name, ' ', last_name)"), 'like', "%{$search}%");
})

Visibility & Toggleability

->hidden(bool|Closure $hidden = true) // hide column
->isHidden(): bool
 
// User-toggleable (column picker)
->toggleable(bool $toggleable = true)
 
// Permission-based
->permission(?string $permission) // visible only if user has permission
->visible(Closure $callback) // custom visibility callback (Closure only)
 
// Per-record cell visibility (redact a single cell by row)
->visibleForRecord(Closure $callback) // fn ($record) => bool

->hidden(), ->permission(), ->visible() and ->authorize() decide whether the column exists in the table at all — they are evaluated once, without a record (they also drive the header, column toggle and export). To hide or redact a single cell per row — e.g. show salary only for records the user may see — use ->visibleForRecord(fn ($record) => …), which runs at cell render with the row's record. A hidden cell renders empty; the column still occupies its place in every other row.

TextColumn::make('salary')
->visibleForRecord(fn ($record) => auth()->user()->can('viewSalary', $record));

Responsive Breakpoints

->visibleFrom(string $breakpoint) // hidden below this breakpoint
->hiddenFrom(string $breakpoint) // hidden from this breakpoint up
->onlyOnMobile() // visible only on mobile (<md)
->onlyOnDesktop() // visible only on desktop (≥lg)
->onlyOnTabletAndUp() // visible from md up
->onlyOnLargeScreens() // visible from xl up
TextColumn::make('phone')
->visibleFrom('md') // hidden on mobile, visible from md
 
TextColumn::make('notes')
->onlyOnLargeScreens() // only visible on xl+

Responsive Display Variants

// Custom render for mobile vs desktop
->mobileDisplayUsing(Closure $fn)
->desktopDisplayUsing(Closure $fn)
->hasResponsiveDisplay(): bool
 
// Where the column lands on a stacked mobile card (see Advanced → Responsive Layout)
->mobileTitle() ->mobileSubtitle() ->mobileMetric() ->mobileMeta() ->mobileDetail()
TextColumn::make('user')
->mobileDisplayUsing(fn ($record) => $record->name)
->desktopDisplayUsing(fn ($record) => "{$record->name} <{$record->email}>")

Value Formatting

->formatStateUsing(Closure $fn) // transform value for display
->displayUsing(Closure $fn) // alias for formatStateUsing
->default(mixed $value) // value when state is null
->placeholder(string $text) // text shown when value is null/empty
->limit(int $chars) // truncate to N characters
->prefix(string $prefix) // prepend text
->suffix(string $suffix) // append text
->html(bool $html = true) // render value as raw HTML
->wrap(bool $wrap = true) // allow text wrapping (default: nowrap)
TextColumn::make('price')
->prefix('$')
->suffix(' USD')
->placeholder('N/A')
 
TextColumn::make('bio')
->limit(100)
->tooltip(fn ($record) => $record->bio) // show full on hover
 
TextColumn::make('content')
->html()
->wrap()
->limit(200)
TextColumn::make('name')
->weight('bold')
->textSize('lg')
 
TextColumn::make('subtitle')
->textSize('sm')
->textColor('gray')
->weight('light')

Width & Alignment

->width(string $width) // CSS width: '200px', '20%', 'auto'
->alignment(string $alignment) // 'left', 'center', 'right'
->alignLeft() // shortcut
->alignCenter() // shortcut
->alignRight() // shortcut

Icons

->icon(string|Icon|null $icon, ?string $position = 'before') // position: 'before' | 'after'
->color(string|Color $color) // static icon/text color (for per-row color use BadgeColumn/IconColumn colorUsing())
TextColumn::make('email')
->icon('mail', 'before')
->color('primary')

URL (Clickable Cell)

->actionUrl(Closure $url, bool $openInNewTab = false) // make the cell a link
TextColumn::make('name')
->actionUrl(fn ($record) => route('users.show', $record), openInNewTab: true)
->color('primary')

Copyable

->copyable(bool $copyable = true) // click-to-copy icon
->copyMessage(string $msg) // feedback text after copy

Tooltip & Description

->tooltip(string|Closure $tooltip) // hover tooltip
->description(string|Closure $desc) // secondary text below value
TextColumn::make('title')
->description(fn ($record) => Str::limit($record->body, 50))
->tooltip(fn ($record) => "Created: {$record->created_at->format('d.m.Y')}")
->summarize(string $aggregate, ?string $label = null)

Available aggregates: 'sum', 'avg', 'count', 'min', 'max', 'range'

See Advanced — Summary for details.

Extra HTML Attributes

->extraAttributes(array $attrs) // on <td>
->extraHeaderAttributes(array $attrs) // on <th>
TextColumn::make('notes')
->extraAttributes(['data-testid' => 'notes-cell'])
->extraHeaderAttributes(['class' => 'bg-gray-100'])

Pivot Columns

->pivot(bool $isPivot = true) // marks as pivot table column
->isPivot(): bool

For many-to-many relationships with pivot data:

TextColumn::make('roles.pivot.assigned_at')
->pivot()
->dateTime('d.m.Y')

State Access

->state(mixed $value) // override state value
->getState(Model $record): mixed // resolve state from record

Custom Rendering (Blade Partials)

Every column owns its state/configuration and delegates markup to a Blade partial under packages/table/resources/views/tables/columns/. The base text cell renders through text.blade.php; each custom-UI column has its own partial (badge, boolean, icon, image, button, toggle, poll, split, stacked, select, text-input-*). Columns never return inline HTML from renderCell() — they call renderView('tables.columns.<name>', [...]).

Two ways to customize the markup:

// 1. Per-column override — point any column at your own Blade view.
TextColumn::make('name')->view('columns.my-name-cell');
 
// 2. Project-wide override — publish the package views and edit the partial.
// php artisan vendor:publish --tag=wire-table::views
// then edit resources/views/vendor/wire-table/tables/columns/badge.blade.php

View resolution order: an explicit ->view() wins, then the package view (wire-table::tables.columns.<name>), then an app-level view of the same name. Your partial receives exactly the data the built-in one does — the already resolved state/config primitives for that column — so you only rewrite the HTML.