Table
BadgeColumn
Renders a cell as a colored pill whose color and icon are derived from the record's state.
On this page
Renders the cell as a colored pill. Reach for it when the value is a state — a
status, a role, a priority — and the colour is what the reader actually scans
for. For a plain value that just needs an accent colour, TextColumn with
->color() is the lighter tool.
use NyonCode\WireTable\Columns\BadgeColumn;
How It Works
The badge asks two questions per cell — what colour and what icon — and answers each by walking a ladder until a rung responds. Colour, in order:
->colorUsing()— the closure runs first and wins outright when it returns anything butnull. Returningnullfor some states hands those back to the rungs below.->colors()— the map, looked up by the state as key. An enum state is unwrapped to its backing scalar first, soStatus::Activefinds the'active'key.- The state's own colour — an enum implementing the
HasColorcontract names its own colour, and no map is needed at all. ->color()— the column's static colour, so setting one is not silently ignored on a stateful column.gray— the neutral floor. A badge always renders with some colour.
Icons walk the same ladder — ->iconUsing(), ->icons(), the enum's
HasIcon contract, then the column's ->icon() — with one difference: there is
no floor. Nothing matching means no icon, and the badge renders as a plain pill.
Four more things worth knowing before writing the chain:
- The label is resolved separately from the colour. The pill's text comes
from the normal column formatting pipeline (
->formatStateUsing(), casts, enum labels), never from the colour map. An enum state without aHasLabelcontract reads as a headline of its case name —InReview→ "In Review". - Pass an array to
->colors(), not a closure. The signature acceptsarray|Closurebecause it is shared with record-aware surfaces (infolist entries evaluate the closure against their record). A column configures itself before any record exists, so a closure map here matches nothing and every badge drops to the floor colour. Use->colorUsing()for dynamic colours. - Cost is per distinct state, not per row. States are low-cardinality by nature, so the rendered markup is memoised by its resolved data: a thousand rows sharing four statuses render four badges. The closures above therefore run per state value, not per record — do not put record-specific logic in them.
- The value is escaped. Like every text cell, the label is escaped unless
the column opts into
->html()— a record value such as<img onerror=…>is text, not markup. Anullor empty state renders the column's empty-cell text, not an empty pill.
Basic Usage
The map is keyed by the state, and each value is the colour to wear for it:
BadgeColumn::make('status') ->colors([ 'active' => 'success', // green badge for 'active' 'banned' => 'danger', // red badge for 'banned' 'pending' => 'warning', // yellow badge for 'pending' 'draft' => 'gray', // gray badge for 'draft' 'featured' => 'primary', // blue badge for 'featured' 'processing' => 'info', // cyan badge for 'processing' ])
A state the map does not mention falls through the ladder above. Values may also
be given as the Color enum ('active' => Color::Success), and the whole
Tailwind palette is available — see Theming.
With Icons
->icons() is keyed by the state the same way, and falls back to the column's
own ->icon() when the state maps to nothing — an unset ->icon() means no
icon. An enum state implementing the HasIcon contract picks its own icon with
no map at all.
BadgeColumn::make('priority') ->colors([ 'critical' => 'danger', 'high' => 'warning', 'medium' => 'info', 'low' => 'gray', ]) ->icons([ 'critical' => 'exclamation', 'high' => 'arrow-up', 'medium' => 'minus', 'low' => 'arrow-down', ])
Dynamic Colors
When the colour is a function of the value rather than a fixed vocabulary — a score, an amount, an age — derive it. The closure receives the state and runs before the map:
// Closure-based color resolutionBadgeColumn::make('score') ->colorUsing(fn (int $state) => match(true) { $state >= 90 => 'success', $state >= 70 => 'info', $state >= 50 => 'warning', default => 'danger', }) ->iconUsing(fn (int $state) => $state >= 90 ? 'star' : null)
Enum States
An enum that implements the HasLabel / HasColor / HasIcon contracts
carries its own presentation, and the column needs no maps at all. The same enum
then reads identically on a table cell, an infolist entry and a <select>
option:
use NyonCode\WireCore\Foundation\Contracts\Enum\HasColor;use NyonCode\WireCore\Foundation\Contracts\Enum\HasIcon;use NyonCode\WireCore\Foundation\Contracts\Enum\HasLabel; enum OrderStatus: string implements HasColor, HasIcon, HasLabel{ case Pending = 'pending'; case Shipped = 'shipped'; case Cancelled = 'cancelled'; public function getLabel(): string { return match ($this) { self::Pending => 'Awaiting payment', self::Shipped => 'On its way', self::Cancelled => 'Cancelled', }; } public function getColor(): string { return match ($this) { self::Pending => 'warning', self::Shipped => 'success', self::Cancelled => 'danger', }; } public function getIcon(): ?string { return $this === self::Cancelled ? 'x-circle' : null; } } // With the attribute cast to the enum, the column is just the attribute.BadgeColumn::make('status')
A map still beats the enum's own colour, which is how one table can present a shared enum differently without touching the enum.
Custom Label + Badge
->formatStateUsing() rewrites the pill's text without touching the colour
ladder — the map stays keyed by the raw state:
BadgeColumn::make('role') ->formatStateUsing(fn (string $state) => match($state) { 'super_admin' => 'Super Admin', 'admin' => 'Administrator', 'editor' => 'Editor', default => ucfirst($state), }) ->colors([ 'super_admin' => 'danger', 'admin' => 'primary', 'editor' => 'success', ])
Size
BadgeColumn::make('tag') ->size('xs') // xs, sm, md, lg — default md
->xl() exists on the shared size API, but the badge surface renders it with
the md padding; lg is the largest pill.
Extended Example
A moderation table where one column carries three signals at once: the colour comes from a map, the icon marks only the states that need attention, and the label is rewritten for readers who do not think in slugs.
use Livewire\Component;use NyonCode\WireTable\Concerns\WithTable;use NyonCode\WireTable\Table;use NyonCode\WireTable\Columns\BadgeColumn;use NyonCode\WireTable\Columns\TextColumn; class ArticleTable extends Component{ use WithTable; public function table(Table $table): Table { return $table ->model(Article::class) ->columns([ TextColumn::make('title') ->searchable() ->weight('bold'), BadgeColumn::make('status') ->colors([ 'published' => 'success', 'in_review' => 'warning', 'rejected' => 'danger', 'draft' => 'gray', ]) ->icons([ 'in_review' => 'clock', // only the states that need a 'rejected' => 'x-circle', // second glance get an icon ]) ->formatStateUsing(fn (string $state) => str($state)->headline()) ->size('sm'), TextColumn::make('published_at') ->dateTime('d.m.Y') ->sortable(), ]) ->defaultSort('published_at', 'desc') ->paginated(); } public function render() { return view('livewire.article-table'); }}
BadgeColumn API
The badge surface itself. Everything else a column can do — ->label(),
->sortable(), ->visible(), formatting, editing — is the shared column API,
documented in Columns.
->colors(array $map) // ['state' => 'color_name'|Color, ...]->colorUsing(Closure $fn) // fn ($state) => 'color_name'|Color|null — beats the map->icons(array $map) // ['state' => 'icon_name'|Icon, ...]->iconUsing(Closure $fn) // fn ($state) => 'icon_name'|Icon|null — beats the map->color(string|Color $color) // fallback colour when the state maps to nothing->icon(string|Icon $icon) // fallback icon when the state maps to nothing->size(string|Size $size) // 'xs'|'sm'|'md'|'lg' — default 'md'->xs() / ->sm() / ->md() / ->lg() // size presets->getSize(): string->getColorForState($state): ?string // the resolved colour, whole ladder included->getIconForState($state): ?string // the resolved icon, whole ladder included
Related
- Columns — the shared column API every column inherits
- IconColumn — the same state ladder, rendered as an icon alone
- PollColumn — a badge over a live-polled value
- Theming — the colour vocabulary these maps draw from