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

optimistdigital/nova-media-field

A Laravel Nova field for attaching and managing media on your resources. Upload images or files, preview selections, and store them with your models, giving editors a simple media picker inside the Nova admin panel.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish

composer require optimistdigital/nova-media-field
php artisan vendor:publish --provider="OptimistDigital\NovaMediaField\NovaMediaFieldServiceProvider"
php artisan migrate

The package ships a service provider, a config file (thumbnails, disk, storage path) and a migration stub that creates the table holding media records. Publish and migrate before touching a resource — the field resolves its value against that table, so a missing migration surfaces as an empty field rather than an error.

Requirements to verify on the tag you install: Nova 2/3-era API, Laravel 8/9 range, GD or Imagick for thumbnail generation. Check composer.json in the repo for the exact constraints before adding it to a Nova 4/5 app — this package is archived (last release January 2023) and predates several breaking Nova field changes.

Register the field on a resource

use OptimistDigital\NovaMediaField\MediaField;

public function fields(Request $request)
{
    return [
        ID::make()->sortable(),
        Text::make('Title'),

        MediaField::make('Images')
            ->multiple()                 // single media if omitted
            ->rules('array')             // see gotchas
            ->help('Upload new files or pick from existing media'),
    ];
}

Add the matching column to the resource's table (json on MySQL/Postgres preferred over text; cast it as array on the model).

Where to look first

  1. README.md — the package is small enough that the README is close to complete documentation.
  2. src/MediaField.php — the entire public API surface (methods like multiple(), thumbnail sizing, disk selection). Read it end to end; it's one class.
  3. config/nova-media-field.php — thumbnail dimensions, storage disk, allowed mime types, max upload size.
  4. database/migrations/* — the exact schema of the media table. This is the most important file, because it defines the contract you'll write accessors, seeders and cleanup jobs against.
  5. resources/js/ — the Vue field component; only relevant if you need to fork or restyle.

First use case

The canonical one: a Post resource with a cover image plus a gallery. Replace a bare File::make('Image') with MediaField::make('Images')->multiple() and you immediately get upload + browse + thumbnails + delete in one control, without writing a media model, upload controller or index preview yourself. A 30-minute spike — install, upload three images, then dd() the saved column and the media table rows — tells you everything you need to know about the storage contract before you build on it.

Implementation Patterns

Contract-first development. Before writing frontend code, inspect what the field persists: typically the field column stores a reference (ID or path) or a JSON array of them, and a separate media table stores file metadata. Write a small read-only tinker session against a real upload, then build accessors around what you find rather than around what you assume.

Model accessors and casts. Keep the field's raw value in the database and expose clean URLs from the model:

protected $casts = ['images' => 'array'];

public function imageUrls(): array
{
    return collect($this->images ?? [])
        ->map(fn ($item) => Media::find($item)?->url())  // adjust to actual API
        ->filter()
        ->values()
        ->all();
}

Centralising this in one place means a future migration to spatie/laravel-medialibrary only touches the model, not every Blade template.

Thumbnail strategy. Configure fixed thumbnail sizes in the config file and let the package generate them on upload, then reuse those sizes everywhere — index column, form preview, Blade card, OG image. Regenerating on the fly is the classic performance trap: an index listing 50 rows with dynamic resizing will hammer GD/Imagick.

Reuse across resources. The browsing feature is the real value: an editor picks an existing image instead of re-uploading it. Lean into this by standardising on one field name per collection (e.g. gallery, hero) so media stays discoverable across resources, and by treating the media table as a shared library rather than per-model storage.

Index and detail previews.

MediaField::make('Images')
    ->multiple()
    ->thumbnail(fn ($value) => /* first image URL */)
    ->onlyOnIndex(),

If the field doesn't expose a display callback in your version, use ->displayUsing(...) or a Stack of Image fields instead — keep the heavy media field on forms only.

Programmatic attachment. Seeders, importers and API endpoints should create media through the package's model rather than by guessing at the schema. Wrap it in a small MediaService so a future replacement of the package is a single-file change.

Deletion semantics. Decide early whether removing a media item from a field detaches it (record stays, reusable) or deletes the file. The field usually offers both via a config flag or per-field method. For a shared library, detach-only is almost always correct; file deletion is a separate admin action.

Frontend rendering.

@foreach ($post->imageUrls() as $url)
    <img src="{{ $url }}" alt="" loading="lazy">
@endforeach

Cache the URL list per model in a MediaPresenter if you render it in loops.

Gotchas and Tips

  • Archived package. No upstream fixes, no Nova 4/5 compatibility guarantees. Pin the exact version in composer.json, and plan to fork (repositories with a VCS entry) rather than wait for a patch. Budget a day for a Nova 4+ migration: field metadata, asset loading and Field base-class changes all moved.
  • Provider/namespace drift. The namespace is version-sensitive (OptimistDigital\NovaMediaField\... in the newer tags, OptimistDigital\MediaField\... in some builds). If composer require succeeds but the class isn't found, grep vendor/optimistdigital/nova-media-field/src for the actual FQCN before debugging further.
  • Validation against arrays. ->rules('required') on a multiple field validates the array wrapper, not membership. Use 'required|array|min:1' and 'images.*' => 'integer|exists:media,id' (adjust table name) so a stale ID doesn't 500 the page.
  • storage:link and disks. Thumbnails written to public need php artisan storage:link. On S3, ensure the disk's url and visibility are set (public or signed URLs) or you'll get 403s in the Nova index while uploads appear to succeed.
  • Config caching. Thumbnail sizes and disk are read from config. Run php artisan config:clear after editing config/nova-media-field.php; a cached config is the most common "my new thumbnail size doesn't exist" cause.
  • Existing media aren't re-thumbnailed. Adding a new size to config only affects new uploads. Write a one-off artisan command that iterates the media table and regenerates, and queue it for large libraries.
  • Orphaned records. Deleting a Post doesn't delete its media rows or files. Add an observer (deleted → detach/cleanup) or a scheduled media:prune command; otherwise the table and disk grow silently.
  • Column type. Use json, not varchar(255) — multiple images blow past 255 characters fast, and MySQL will truncate rather than error in non-strict mode.
  • Filename collisions and unicode. Two uploads named photo.jpg can overwrite each other depending on the package's naming. Check the generated filename strategy and consider prefixing with a UUID in a custom upload handler if you accept user-supplied names.
  • Thumbnailing is synchronous. Large images (10MB+) block the request and can hit PHP's max_execution_time. Cap upload size in config, or move to a queued conversion if editors routinely upload camera originals.
  • Field assets and Mix/Vite. If you fork, rebuild the JS (npm install && npm run prod) and publish assets. Stale compiled assets after a fork are a silent failure — the field renders but the upload button does nothing.
  • Debugging checklist when the field is blank: is the migration run, is the column cast to array, does the field name match the column, is the disk public, and do the media rows actually exist?
  • Modern alternative. For greenfield projects, spatie/laravel-medialibrary plus one of the maintained Nova media fields gives you conversions, responsive images, S3 support and PHP 8.3+/Nova 4+ compatibility. Use this package when the app is already on it and the cost of migration outweighs the maintenance risk — not for new builds.
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.
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
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect