filament/infolists
Filament Infolists provides a rich set of read-only display components for Laravel admin panels—text, badges, images, icons, key-value entries, and more—making it easy to build clear resource view pages and detail screens with Filament's familiar, elegant API.
Infolists are Filament's read-only "display" layer: an ordered schema of entries that renders a record's data. They ship inside the filament/filament meta-package, so if you already have a panel installed you can start using them immediately — no extra composer require. For a bare Livewire app, the standalone filament/infolists package pulls in filament/schemas and filament/support.
First place to look is docs/01-overview.md (entries, state resolution, utility injection), then skim the entry list — TextEntry, ImageEntry, IconEntry, ColorEntry, KeyValueEntry, RepeatableEntry — plus the layout components (Section, Grid, Fieldset, Tabs, Split). Custom entries are covered in docs/09-custom-entries.md.
The fastest first use case is a Resource view page or a table "View" modal. On a ViewRecord page (v4/v5 signature):
use Filament\Infolists\Components\ImageEntry;
use Filament\Infolists\Components\TextEntry;
use Filament\Schemas\Schema;
public function infolist(Schema $schema): Schema
{
return $schema->components([
Section::make('Profile')->columns(2)->components([
ImageEntry::make('avatar')
->circular()
->imageSize(40)
->defaultImageUrl(url('storage/avatars/default.jpg')),
TextEntry::make('name')->weight('bold'),
TextEntry::make('email')->copyable(),
TextEntry::make('created_at')->dateTime('M j, Y'),
]),
]);
}
On Filament v3 the same page method receives an Infolist $infolist and you chain ->schema([...]) instead of ->components([...]). Check your major version before copy-pasting docs.
Record-backed vs. standalone. The common case binds entries to the page's Eloquent record (TextEntry::make('name') resolves $record->name). The second case is embedding a standalone infolist in any Livewire component: add the InteractsWithSchemas trait, define infolist(Schema $schema), call ->record($this->user), and render {{ $this->infolist }} in the view. This is the pattern for "show this customer's details" cards, order summaries, and audit panels outside the admin panel.
Embedding inside forms. Infolist::make('summary')->components([...]) can live inside a form schema, which is how you build edit pages with a read-only header block. Use ->record() explicitly if the infolist is not the page's main schema.
Dot notation for relationships. TextEntry::make('author.name') and ImageEntry::make('author.avatar') just work. Prefer eager loading in getRecord()/modifyQueryUsing() rather than relying on per-entry lazy loads.
Derived state. When the value isn't a column, override it with a closure and let utility injection hand you the record:
TextEntry::make('posts_count')
->state(fn (User $record): int => $record->posts()->count())
->badge();
The same injection applies to nearly any configuration method: ->color(fn ($state) => ...), ->url(fn (Post $record) => route('posts.show', $record)), ->visible(fn ($record) => $record->isPublished()). Parameter names matter — $record, $state, $component, $livewire, $operation are resolved by name.
Formatting pipeline. formatStateUsing() → dateTime()/money()/limit()/listWithLineBreaks()/bulleted() → presentation (badge(), color(), icon(), weight(), size(), copyable(), tooltip(), suffixAction()). For enums, implement HasLabel/HasColor/HasIcon on the enum and Filament picks them up automatically.
Layout. Section is your card; Grid::make(3) plus columnSpan() / columnSpanFull() is how you get real dashboards. Split gives a 2/3 + 1/3 layout, Tabs is good for dense records (Overview / Billing / Activity) so you don't render 60 entries at once.
Reusable custom entries. php artisan make:filament-infolist-entry AudioPlayerEntry scaffolds a class plus a Blade view. Add fluent setters that store into nullable properties and return static, and getters that call $this->evaluate() so consumers can pass closures:
protected float | Closure | null $speed = null;
public function speed(float | Closure | null $speed): static
{
$this->speed = $speed;
return $this;
}
public function getSpeed(): ?float
{
return $this->evaluate($this->speed);
}
Any public method on the entry becomes a $getSpeed() helper in the view. This is the cleanest extension point for charts, embedded players, maps, and QR codes.
Server round-trips from JS. Mark a public method #[ExposedLivewireMethod], add #[Renderless] if the UI doesn't need to re-render, and call $wire.callSchemaComponentMethod(@js($getKey()), 'methodName', ...args) from Alpine. Useful for lazy-loading waveform data or previews without building a whole Livewire child component.
$getState(), $get('otherField'), $record, $operation, $this (the Livewire component), and $entry. Every custom view must be wrapped in <x-dynamic-component :component="$getEntryWrapperView()" :entry="$entry">, otherwise label, alignment, and columnSpan silently break.$get() is stale until a request happens. Reading another component's state only refreshes on the next round-trip; make the source field live() if you need reactive updates. Similarly, ->state(fn () => ...) overrides the record value and won't recompute on every keystroke.public / visibility('public'), Filament generates temporary URLs (S3 presigned). Don't cache rendered infolist HTML, and set disk() explicitly when you're not on FILESYSTEM_DISK=local. defaultImageUrl() covers nulls but not broken paths.hidden()/visible() are cosmetic, not authorization. The record is still hydrated server-side. Gate sensitive data with policies and simply don't add the entry — don't rely on ->hidden(fn () => ! auth()->user()->isAdmin()) as your only control. The same applies to copyable() on tokens and emails.TextEntry with "Array to string conversion". Reach for listWithLineBreaks(), bulleted(), or separator(', '). Enums need formatStateUsing(fn ($state) => $state?->label()) unless the enum implements Filament's contracts.ViewAction modal resolving ten relationship entries against 25 table rows is 250 queries. Eager load in the table query, or hoist the shared infolist into a single method both the view page and the action use.Infolist $infolist + ->schema(); v4/v5 use Schema $schema + ->components(). The docs site defaults to the latest major — check the version selector when a snippet "doesn't exist".->state(function ($record) { dd($record); }) or ->visible(fn ($state) => dd($state)) into a suspicious entry to see exactly what Filament resolved and when. For layout issues, inspect the rendered wrapper — most "my entry has no label / is full width" reports trace back to a custom view missing $getEntryWrapperView().Livewire::test(ViewUser::class)->assertSee('[email protected]') catches most regressions. Assert on the rendered string rather than on entry configuration — fluent configuration is easy to unit test but proves nothing about what a user sees.->extraAttributes() for hooking CSS/JS, FilamentView::registerRenderHook() for injecting markup around sections, and view namespaces if you're distributing reusable entries as a plugin. Register custom entry views under a consistent namespace so published views don't collide with app views.How can I help you explore Laravel packages today?