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

Nova Gallery Laravel Package

ericlagarda/nova-gallery

A Laravel Nova package that adds gallery and multi-image management to your admin panel. Upload, organize, and display image collections directly on Nova resources with a simple field, streamlining media handling for Eloquent models.

View on GitHub
Deep Wiki
Context7

Getting Started

NovaGallery is a Nova field that replaces a plain Image/File field with a multi-image gallery UI: upload several photos, drag to reorder, delete individually. It was built for the Nova 2.x era (last release April 2019), so the first job is confirming it still renders in your Nova version.

Minimal path to a working gallery:

  1. composer require ericlagarda/nova-gallery
  2. Publish the package's migrations and assets, then migrate:
    php artisan nova-gallery:install   # or vendor:publish, check the README
    php artisan migrate
    
    The package ships its own table(s) rather than writing a JSON blob onto your model, so you need the schema in place before the field does anything.
  3. Link the storage disk: php artisan storage:link (Nova serves uploads through the public disk by default).
  4. Add the field to a resource:
    use Krato\NovaGallery\NovaGallery;
    
    public function fields(Request $request)
    {
        return [
            ID::make()->sortable(),
            Text::make('Name'),
            NovaGallery::make('Gallery'),
        ];
    }
    

Where to look first: the published config/nova-gallery.php (disk, storage path, allowed mime types) and the generated migration. Those two files tell you almost everything about how files are named and where they land. Then open the field in a detail view and upload one image before wiring up anything else — that single upload either works or fails on the storage/asset layer, and it's the fastest way to separate "package is broken on my Nova version" from "my disk is misconfigured."

First real use case: a product or listing with a set of ordered photos. This is where a gallery field earns its keep over a repeatable File field — you get ordering and thumbnails for free, and the admin can curate the sequence without touching a separate relation UI.

Implementation Patterns

Treat the field as the uploader, your app as the renderer. The package owns the upload/reorder UI and the persistence of file references; it does not give you a front-end component, image resizing, or a fluent relationship API. The common pattern is to let the gallery store references, then read them in a model accessor or API resource:

// On the model that owns the gallery
public function galleryImages(): array
{
    return $this->gallery()->orderBy('order')->pluck('path')->all();
}

Confirm the exact column and relation name against the published migration before relying on it — the package's internal naming is not part of a documented public contract and changed across its short history.

Pair it with a transformation layer. Because the field stores originals, day-to-day use almost always means pairing it with spatie/laravel-medialibrary or Glide/Intervention for responsive sizes. A pragmatic split: use NovaGallery for the admin upload/reorder experience, and run a queued job (or a model observer) that copies each uploaded file into your own Media model at the sizes your front-end needs. That keeps the package at arm's length — if you later drop it, your display code is untouched.

Validation and constraints live on the field, not the package. Apply accepted mime types and size limits through the field's rules so the Nova form rejects bad uploads before they hit disk:

NovaGallery::make('Gallery')
    ->rules('array', 'max:12')
    ->help('Up to 12 images, JPG or PNG, 5MB each');

The package's own mime checking should be treated as a convenience, not a security boundary — re-validate on the server side for anything user-facing.

Index and detail views differ. Nova fields render differently on the index; a gallery on an index column usually shows a single thumbnail or a count. Don't assume the full grid renders there. If you need a richer preview, override the field's index component or fall back to a Text field showing the image count.

Integrate with your resource's lifecycle explicitly. Deleting a parent record does not reliably clean up the gallery rows or the files on disk in packages of this vintage. Add a model deleting observer that removes the gallery rows and calls Storage::disk(...)->delete(...) on each path. This is the single most valuable piece of glue you'll write around the package.

Reordering is the workflow feature. The drag-to-reorder is what differentiates the field from a stack of File inputs. Surface that order in the front-end query with an explicit orderBy rather than relying on insertion order — the package writes a sort column precisely so you can.

Gotchas and Tips

Nova version compatibility is the headline risk. A 2019 release targets Nova 2.x's Vue components and field API. Nova 3 and especially Nova 4 changed the asset pipeline and field lifecycle enough that the compiled JS may not load, toolbars may not register, or the field may render blank. Before committing, install it in a throwaway branch and click through upload → reorder → delete on a real Nova detail page. If it renders blank, check the browser console for a missing Nova.booting registration — that's the usual symptom of an old field against a new Nova.

Check the disk config against filesystems.php. The most common "upload succeeds but nothing shows" bug is a disk whose url doesn't match the storage symlink, or a private disk being served to the browser. The package reads from the published config; if you change the disk in one place, change it in the other.

File cleanup is on you. As above — assume no cascade delete. Also assume no orphan cleanup when a user uploads then removes an image in the same session before saving; verify the behavior in the storage directory and add a sweep job if needed.

No image processing. Don't expect thumbnails to be generated at other sizes. The grid thumbnails are CSS-scaled originals unless you add resizing yourself. For a gallery with 10MB photos this is a real performance problem in the admin.

Debugging uploads: open the network tab on the upload POST and look at the response body — the package surfaces validation failures as a generic toast. The actual error (mime rejection, disk permission, missing table) is in the JSON response. Locally, confirm storage/app/public is writable and the symlink exists; in production, confirm the same on the release user.

Extension points are thin. You can publish and override the package's Vue components and views to customize the markup, and you can swap the field's config values. There is no event system, no fluent API for image transforms, and no documented contract for the storage schema. If you need behavior beyond "upload, order, delete," plan to fork the field rather than subclass it — and if you fork, pin the version in composer.json and vendor the fork, because upstream is unlikely to merge anything.

Maintenance posture. Nine stars, zero dependents, and no release since April 2019 means you are effectively the maintainer. Budget for that: read the whole source (it's small), lock the version, and keep a fallback path — often a custom Nova field backed by spatie/laravel-medialibrary is only a day's work and is the safer long-term bet if the gallery is central to your product. Use NovaGallery when you need a working gallery this week and the Nova version lines up; build the custom field when the gallery is core.

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.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
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