Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Stacked Image Column Laravel Package

archilex/filament-stacked-image-column

Filament Stacked Image Column displays multiple images as overlapping stacked thumbnails in Filament tables, with configurable overlap, sizing, and fallbacks. A lightweight, easy-to-install package for richer image columns in Laravel admin panels.

View on GitHub
Deep Wiki
Context7

Getting Started

archilex/filament-stacked-image-column gives you a Filament table column that renders an array of image paths as an overlapping "avatar stack" instead of a single thumbnail. Day-to-day, it replaces writing a custom TextColumn with formatStateUsing() + HtmlString whenever a record has many images.

Install and register:

composer require archilex/filament-stacked-image-column

No service provider or config publishing is required — it's a column class you reference directly in a resource table:

use Archilex\StackedImageColumn\Columns\StackedImageColumn;

StackedImageColumn::make('photos')
    ->disk('public')
    ->size(32)
    ->circular()
    ->limit(4),

Look first at the package README for the exact method list for your installed version, then at src/Columns/StackedImageColumn.php — it's a thin subclass of Filament's ImageColumn, so everything you already know about ImageColumn (disk(), visibility(), size(), defaultImageUrl()) carries over and only the stacking behaviour is new.

The canonical first use case is a Spatie MediaLibrary collection or a JSON photos column on a model where you want a compact preview in the table and the full gallery on the View/Edit page. Start there, confirm it renders, then tune size/overlap/limit.

Implementation Patterns

From a JSON array column. Cast the attribute in the model and point the column at it:

// Model
protected $casts = ['photos' => 'array'];

// Resource
StackedImageColumn::make('photos')->disk('public')->limit(5)

From a MediaLibrary collection. The column needs URLs or paths, not Media models, so map them and eager load to avoid N+1:

StackedImageColumn::make('media')
    ->getStateUsing(fn ($record) => $record->getMedia('gallery')->map->getUrl())

In the resource's getTableQuery() (or modifyQueryUsing), add ->with('media').

Mixed sources / computed stacks. Anything that resolves to an array of URLs works, so you can merge a primary image with fallbacks:

->getStateUsing(fn ($record) => collect([$record->cover_url])
    ->merge($record->photos ?? [])
    ->filter()
    ->values())

Placeholder handling (new in 1.1.0). defaultImageUrl() now works on the stacked column, so an empty array renders a placeholder rather than collapsing the cell:

StackedImageColumn::make('photos')
    ->defaultImageUrl(asset('images/placeholder.png'))

Pairing with the record view. A common pattern is a small stack in the table and the full set on the infolist/form — keep both driven by the same getStateUsing() closure or a model accessor so the two never drift.

Sizing consistency. Use the same size() value across your resource's avatar/image columns so table rows keep a uniform height; the stack's height is driven by the largest single image plus its ring.

Gotchas and Tips

  • Upgrade if you use local/public disks. v1.1.0 fixes stacked images stored on local storage (PR #7). On v1.0.0, paths resolved against the wrong URL generator and local files rendered broken while S3/remote disks looked fine. This is the main reason to bump; it's a patch-level fix with no API change.
  • defaultImageUrl() is column-wide, not per-item. It handles the empty-collection case; it will not backfill a single missing file inside an otherwise populated array. Filter or replace those entries in getStateUsing() yourself.
  • Return an array, not a JSON string. If you skip the 'photos' => 'array' cast you'll get a string and a single broken image — a classic five-minute debug.
  • Eager load the media relation. getMedia() inside getStateUsing() runs per row; without with('media') a 50-row table issues 50 queries.
  • Clipping and rings. The stack uses negative margins; overflow-hidden on the table cell or a custom row view can shear off rings and shadows. If the stack looks cut off, inspect the wrapping td before touching column options.
  • limit() + remainder indicator. Setting a limit usually pairs with an "and N more" affordance; if you need the count, compute it in getStateUsing() or a separate column rather than expecting the column to expose it.
  • Version-pin carefully around Filament majors. The column subclasses ImageColumn, so a Filament v4 upgrade can shift inherited methods (size(), defaultImageUrl()) out from under it. Test the stack in a branch before bumping Filament.
  • The Dependabot entry in 1.1.0 is CI-only (fetch-metadata) and has zero runtime impact — don't read it as a functional change.
  • Debugging tip. Dump what the column receives with ->getStateUsing(fn ($record) => tap($yourArray, fn ($v) => dump($v))); almost every visual bug is a wrong-shaped state, not a rendering bug.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource