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 Media Library Laravel Package

jameslkingsley/nova-media-library

A Laravel Nova integration for spatie/laravel-medialibrary that lets you upload, manage, and attach media to Eloquent models directly from Nova, with file previews and support for multiple media collections.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is a single Nova field (Jameslkingsley\NovaMediaLibrary\MediaLibrary) that renders a Spatie Media Library collection as an upload/preview widget and attaches uploads to the parent model. It is unmaintained (last release January 2019), so treat version selection as step one, not an afterthought.

1. Pin the stack before installing. The field was built for Nova 1.x/2.x and medailibrary 7.x on PHP 7.x. In composer.json you generally need something like laravel/nova: ^2.0, spatie/laravel-medialibrary: ^7.0, and jameslkingsley/nova-media-library: ^1.0. Installing it against Nova 4 or medialibrary 10 will either fail to resolve or produce a field whose Vue 2 assets never mount.

2. Install and publish the media migrations if you have not already:

composer require jameslkingsley/nova-media-library
php artisan vendor:publish --provider="Spatie\MediaLibrary\MediaLibraryServiceProvider" --tag=migrations
php artisan migrate
php artisan storage:link

3. Prepare the model. The field does nothing useful unless the model implements Spatie\MediaLibrary\HasMedia and uses InteractsWithMedia, and the collection it writes to must be registered:

use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;

class Post extends Model implements HasMedia
{
    use InteractsWithMedia;

    public function registerMediaCollections(): void
    {
        $this->addMediaCollection('images');
    }
}

4. Add the field to the resource.

use Jameslkingsley\NovaMediaLibrary\MediaLibrary;

public function fields(Request $request)
{
    return [
        MediaLibrary::make('Images'),
    ];
}

5. Look here first when something breaks. Because the README is thin, the source of truth is vendor/jameslkingsley/nova-media-library/src/MediaLibrary.php (available field methods, default collection/disk) plus its routes/ and resources/js/ directory (the upload endpoint and what payload the Vue component posts). Read those two before guessing at method names.

First use case to prove out: a cover single-image collection on one model — upload through the field, confirm a media row appears, confirm the file lands on the configured disk, then confirm the same image renders on the detail view. If that loop works, the rest of the package is variations on it.

Implementation Patterns

Collection-per-field. The cleanest pattern is one media collection per field, declared explicitly rather than relying on the field's default collection name:

MediaLibrary::make('Gallery')->collection('gallery')

and a matching addMediaCollection('gallery') on the model. This keeps multiple media fields on the same resource from fighting over the same rows, and makes registerMediaConversions / registerMediaCollections readable.

Single vs. multiple. For a cover image, declare the collection as single-file on the model (->singleFile() in medialibrary 7+, or however the field exposes its single mode) so a second upload replaces the first instead of stacking. For galleries, keep it multi-file and add ->sortable() on the model's collection if you want drag-ordering in the admin. Mixing these up is the most common source of "why do I have twelve covers" bugs.

Conversions for the Nova index. Uploads are full-size originals. Define the sizes you actually render in the admin and in your front end, then use them everywhere instead of the original:

public function registerMediaConversions(Media $media = null): void
{
    $this->addMediaConversion('thumb')->width(300)->height(300)->nonQueued();
}

Anything the Nova index or detail view displays should point at a thumb-style conversion; otherwise a 12MB phone photo gets shipped to every row of your resource table.

Bridging to plain Nova fields. The field handles the media itself, but you often still want a text column for alt text or credit. The usual pattern is a normal Text::make('Alt text') beside the media field, resolved on the model from the first media item's custom properties, and written back in the model's saving/saved hook. Don't try to make the media field carry non-media data.

Scoping and authorization. Media fields are expensive to render, so gate them with ->canSee() / ->canBeSeeable() where the role does not need them, and hide them on index/detail if you are not eager-loading. A common split is: full upload widget on forms, a read-only preview (resolveUsing on the first media URL, or a plain Image field) on index and detail.

Import and seed paths. Do not route bulk imports through the Nova field. Use Spatie's API directly ($model->addMediaFromUrl(...)->toMediaCollection('gallery')) in seeders, console commands, and imports; the field is a UI layer over the same API and adds nothing off-request.

Replacing it incrementally. If you need behaviour the archived field does not offer (video, ordering UI, per-item metadata), the low-risk route is to keep the model side on Spatie and swap only the Nova field for a maintained one (ebess/advanced-nova-media-library is the usual successor). Because the database schema and collections stay put, that swap is a resource-file change, not a migration.

Gotchas and Tips

  • It is archived. There will be no fixes. Before adopting, confirm your Nova and medialibrary majors actually resolve together; if you are on Nova 4+ or medialibrary 8+, expect to either pin your whole admin stack backwards or not use this package at all. Check composer why-not output rather than assuming.
  • Unregistered collection = silent failure. Uploads go to a collection name the field assumes. If registerMediaCollections() does not declare that exact name, you get an exception or a file that vanishes from the UI. Log the collection name the field posts (browser network tab, the upload request payload) and match it.
  • Model contract is mandatory. Missing implements HasMedia / use InteractsWithMedia produces confusing 500s from the field's endpoint rather than a friendly validation error.
  • Disk configuration is global. Files land wherever config/media-library.php says, not where the field says, unless it exposes ->disk(). On S3, remember the field's preview URLs and any storage:link setup must agree; broken thumbnails in Nova are almost always a disk/URL mismatch, not a field bug.
  • Replace vs. append semantics. Verify by hand what a second upload does in single mode. If the old row is deleted and the new one created, any code holding a media ID (emails, caches, API payloads) breaks silently on re-upload.
  • Index performance. The field will lazy-load media per row unless the resource eager-loads. Add ->withMedia('gallery')-style eager loading (or with('media')) in the resource's indexQuery; a 50-row list otherwise fires 50+ queries.
  • Debugging uploads. Nova field bugs are almost never in PHP. Open the network tab, inspect the POST to the package's route, and read the JSON error. Then mirror that request with curl/dd() in the package's controller — that isolates field-vs-model-vs-filesystem in one step.
  • Vue 2 assets. If the field renders as an empty div, the compiled assets are for the wrong Nova frontend. php artisan nova:publish and a hard browser cache clear fix most of these; if not, the majors are mismatched.
  • Extension points are limited but real. Subclass the field to override resolve/fill or add your own defaults (disk, collection, accept list, max size), and keep that subclass in your app so an eventual replacement is a one-file swap. Everything model-side (conversions, custom properties, responsive images) should be done in Spatie directly, never through the field.
  • Regenerate after config changes. Changing conversion sizes or the disk does not retroactively fix existing files; run php artisan medialibrary:regenerate and expect it to be slow on large libraries.
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.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels