Why the Infolist API Exists
Before Filament introduced the Infolist API, displaying a record's details meant either duplicating form schema into a disabled form or falling back to a custom Blade view. Both approaches break down fast: disabled forms carry validation baggage, and raw Blade views drift out of sync with your resource schema.
The Infolist API gives you a first-class, composable, read-only rendering layer that mirrors the form schema API in structure but is purpose-built for display. This article focuses on the practical patterns that matter in production: custom entries, repeatable sections, and state-driven visibility.
Registering an Infolist on a Resource
Add infolist() to your ViewRecord page or directly on the resource:
// app/Filament/Resources/OrderResource.php
public static function infolist(Infolist $infolist): Infolist
{
return $infolist->schema([
Section::make('Customer')
->columns(2)
->schema([
TextEntry::make('customer.name')->label('Name'),
TextEntry::make('customer.email')->label('Email'),
]),
Section::make('Financials')
->schema([
TextEntry::make('total_amount')
->money('USD')
->label('Total'),
TextEntry::make('status')
->badge()
->color(fn (string $state): string => match ($state) {
'paid' => 'success',
'pending' => 'warning',
default => 'danger',
}),
]),
]);
}
The ->badge() modifier on TextEntry renders the value as a coloured pill — no custom Blade required.
Building a Custom Entry
When built-in entries aren't enough, extend Entry directly. Here's a JsonTreeEntry that renders a JSONB column as an indented key-value list:
// app/Filament/Infolists/Components/JsonTreeEntry.php
namespace App\Filament\Infolists\Components;
use Filament\Infolists\Components\Entry;
class JsonTreeEntry extends Entry
{
protected string $view = 'filament.infolists.components.json-tree-entry';
public function getState(): array
{
$raw = parent::getState();
return is_array($raw) ? $raw : json_decode($raw, true) ?? [];
}
}
{{-- resources/views/filament/infolists/components/json-tree-entry.blade.php --}}
<x-dynamic-component :component="$getEntryWrapperView()" :entry="$entry">
<ul class="text-sm space-y-1">
@foreach ($getState() as $key => $value)
<li><span class="font-medium">{{ $key }}:</span> {{ $value }}</li>
@endforeach
</ul>
</x-dynamic-component>
Register it as a static factory method for ergonomics:
JsonTreeEntry::make('meta')->label('Metadata'),
Repeatable Sections for HasMany Relations
RepeatableEntry iterates a relationship and renders a schema for each child record:
use Filament\Infolists\Components\RepeatableEntry;
RepeatableEntry::make('lineItems')
->label('Line Items')
->schema([
TextEntry::make('product.name')->label('Product'),
TextEntry::make('quantity'),
TextEntry::make('unit_price')->money('USD'),
])
->columns(3),
Filament eager-loads the relationship automatically when the infolist is resolved, so there's no N+1 concern here as long as the relationship is declared on the model.
Conditional Visibility Tied to Eloquent State
Use ->visible() or ->hidden() with a closure that receives the record state:
TextEntry::make('refund_reason')
->label('Refund Reason')
->visible(fn (Order $record): bool => $record->status === 'refunded'),
Section::make('Fraud Flags')
->schema([
TextEntry::make('fraud_score'),
TextEntry::make('flagged_at')->dateTime(),
])
->hidden(fn (Order $record): bool => ! $record->is_flagged),
The closure receives the full Eloquent model, so you can call any method or accessor — no need to push display logic into the controller.
Takeaways
- The
InfolistAPI is a dedicated read-only layer; don't repurpose disabled forms for detail views. - Custom entries need only a view and an optional
getState()override — the wrapper component handles label, help text, and layout automatically. RepeatableEntryhandlesHasManyrelationships cleanly; Filament manages eager loading.->visible()/->hidden()closures receive the Eloquent record, keeping conditional display logic close to the schema definition.- Badge colours on
TextEntryare resolved per-row via closures, making status indicators trivial to implement without custom columns.