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 Panzoom Laravel Package

solution-forest/filament-panzoom

A Filament plugin that adds smooth pan and zoom controls to images and media in your admin panel. Drag to pan, scroll or pinch to zoom, with configurable limits and responsive behavior. Drop it into any Filament resource or form to give users a closer look at uploaded files without leaving the page.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package into an existing Filament app:

composer require solution-forest/filament-panzoom
php artisan filament:assets      # or: php artisan vendor:publish --tag=filament-panzoom-assets

If the package ships a config file (config/filament-panzoom.php), publish it in the same step. There is no service provider registration to do manually — Filament auto-discovers the plugin.

Where to look first

  1. src/ — the component class (typically a PanZoom view/form component). This tells you which chainable setters exist (imageUrl(), alt(), minZoom(), maxZoom(), disabled()).
  2. resources/views/ — the Blade template. Read this before overriding anything; it shows how the JS is bootstrapped and which CSS classes/attributes you can hook into.
  3. resources/js/ — the Alpine/vanilla wrapper around the underlying pan-zoom library. This is where the interaction options map to JS options.
  4. The README's "Usage" block — copy the exact namespace; Filament components are often SolutionForest\FilamentPanzoom\... and a wrong namespace is the #1 first-run failure.

First use case: a receipt/document viewer in a ViewRecord page. Drop the component into an Infolist on your OrderResource so support staff can inspect a scanned receipt full-screen without leaving the admin panel:

use SolutionForest\FilamentPanzoom\Infolists\PanZoomEntry; // confirm namespace in README

Infolists\Components\Section::make('Receipt')
    ->schema([
        PanZoomEntry::make('receipt_url')
            ->imageUrl(fn ($record) => $record->getFirstMediaUrl('receipt'))
            ->maxZoom(5),
    ]),

Get one image rendering and zooming before you touch configuration.

Implementation Patterns

Use it in an Infolist for read-only records. This is the natural fit — the component renders an image URL, it doesn't manage uploads. Pair it with Spatie Media Library or a plain Storage::url() accessor and let the entry's imageUrl() closure do the resolution.

Use it inside a table action modal. The highest-value pattern for "click a row, inspect the scan" workflows:

Tables\Actions\Action::make('viewReceipt')
    ->modalContent(fn ($record) => view('filament.partials.receipt-viewer', [
        'url' => $record->receipt_url,
    ]))
    ->modalWidth('5xl')

Render the PanZoom component in that partial. Keeping the viewer in a modal avoids a full page navigation per inspection, which is where the zoom/pan actually earns its keep.

Use it as a read-only Form field when the image is part of an edit form alongside metadata fields (amount, date, vendor). Set ->disabled() or ->readOnly() so Filament doesn't try to hydrate a file state, and make sure the underlying column has no FileUpload counterpart competing for the same state path.

Feed it a signed/temporary URL, not a raw path. For private disks:

->imageUrl(fn ($record) => Storage::disk('private')->temporaryUrl(
    $record->receipt_path, now()->addMinutes(10)
))

Blade usage for custom pages. If you're outside the schema system (custom Livewire page, dashboard widget), render the package view directly via the registered Blade namespace/component rather than reimplementing the JS. This keeps asset loading and Filament's theme integration intact.

Multiple images: there is generally no built-in gallery/carousel. Compose it yourself — either repeat the component inside a Tabs/Repeater-style layout, or use a Select that swaps the imageUrl() state. Don't expect the component to paginate documents for you.

Pair with ->columnSpanFull() in a Section. Cramming a pan-zoom viewer into a 3-column grid defeats the purpose — the viewport needs width.

Gotchas and Tips

  • Re-publish assets after every upgrade. Stale published JS against a new PHP class is the classic "it renders but won't zoom" symptom. Put php artisan filament:assets in your deploy script, or add it to the Composer post-autoload-dump hooks.
  • Livewire re-renders reset the viewport. Any state change on the page re-renders the component and snaps the image back to its default transform. Wrap the container in wire:ignore, or ensure the component boots its JS on livewire:navigated / Alpine x-init rather than only on first DOMContentLoaded. This bites hardest inside forms where other fields trigger updates.
  • wire:navigate / SPA mode. Confirm the JS re-initialises on navigation. If the viewer works on hard refresh but not after clicking through the panel, this is the cause.
  • Private images and CORS. Signed URLs are fine for <img>, but if the underlying library ever draws to a canvas, a cross-origin S3 domain without CORS headers will blank or taint the canvas. Keep images on the app's own domain if you hit this.
  • Large scans. A 10MB receipt JPEG will feel sluggish regardless of the zoom library. Serve a reasonably sized derivative (2000–3000px on the long edge) and set a sensible maxZoom(). Don't rely on the component to downsample.
  • Dark mode and CSS specificity. The wrapper styles are minimal; if the viewer sits on a dark surface, check the container background — transparent backgrounds around small documents look broken. Override via a custom theme or extraAttributes.
  • Tailwind content paths. If you're using a custom Filament theme, the package's views may need to be added to content in tailwind.config.js for utility classes to survive compilation. Filament's default theme usually handles it; custom themes often don't.
  • Filament major-version compatibility. The package is at a single recent release with few stars. Verify the constraint in composer.json matches your Filament version (v3 vs v4 namespace and schema API differences are real). Pin the version in composer.json rather than floating ^.
  • Debugging order: (1) browser console for JS errors, (2) Network tab — did the asset bundle load 200 or 404?, (3) is the <img> src actually a valid URL (dump it in the Blade), (4) only then look at component options.
  • Extension point: publishing views (--tag=filament-panzoom-views) and overriding the Blade template is the safe customisation path. Forking or patching vendor/ will not survive composer update.
  • Low bus-factor. With ~9 stars and no dependents, treat this as a small dependency: read the source before adopting (it's likely a thin Alpine wrapper you could replicate in ~80 lines), and be prepared to vendor-patch if the maintainer goes quiet.
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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core