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 Advanced Image Laravel Package

marshmallow/nova-advanced-image

Advanced image field for Laravel Nova with cropping, previews, and configurable storage. Easily set dimensions, aspect ratios, and thumbnails through a clean API, giving your admin panel richer, more flexible image management.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and wire it up

composer require marshmallow/nova-advanced-image
php artisan vendor:publish --tag="nova-advanced-image-assets"

The vendor:publish step is not optional — this field ships front-end JS/CSS for the crop UI, and without publishing the assets the crop modal silently fails to bind (you'll get a plain file input with no crop trigger). If the package exposes a config tag, publish that too and read it before touching the field; storage disk and crop output settings usually live there.

Look first at

  1. vendor/marshmallow/nova-advanced-image/src/ — the Field class is the source of truth for available fluent methods. Small packages like this (3 stars, zero dependents) often have sparse docs, so read the field class itself; it's usually under 200 lines.
  2. README.md on the GitHub repo for the exact method names (crop(), resize(), aspectRatio(), disk(), path() — verify, don't assume).
  3. resources/js/ in the package if you need to understand what the crop UI actually posts back (useful when debugging null crops).

First use case — a hero image with a fixed aspect ratio

The canonical day-one scenario is a CMS hero/banner that must be cropped to a fixed ratio so marketing can't upload a 4000×300 portrait shot:

use Marshmallow\NovaAdvancedImage\AdvancedImage;

AdvancedImage::make('Hero Image', 'hero')
    ->disk('public')
    ->rules('required', 'image', 'max:5120')
    ->help('Crop to 16:9 — the front end renders a max of 1920px wide.'),

Register it exactly like a native Image field, run a normal nova:asset build (or rely on Nova's auto-discovery in newer versions), and save once. If a crop modal appears and the stored file reflects the crop, you're wired correctly.

Implementation Patterns

Treat it as a drop-in for Nova\Fields\Image

The highest-leverage pattern: keep your existing migration and storage layout (hero string column, disk public) and swap only the field class in the Nova resource. The crop is applied before storage, so your model, accessor, and template code don't change. This makes adoption low-risk and reversible.

One field per ratio, multiple columns

Rather than trying to coax a single field into every context, define distinct fields for distinct crops:

AdvancedImage::make('Card Thumb', 'thumb')->crop(400, 400)->disk('public'),
AdvancedImage::make('Open Graph', 'og_image')->crop(1200, 630)->disk('public'),
AdvancedImage::make('Hero', 'hero')->crop(1920, 1080)->disk('public'),

This is clearer to editors (each has a labelled crop area), keeps the model predictable, and lets you apply per-column validation.

Pair with an index/thumbnail view

Show the field's underlying value on the index with ->thumbnail(fn () => $this->hero_url) so editors can spot broken or uncropped images without opening each record. If the package exposes a preview()/thumbnail() helper, use it — otherwise a small closure returning an <img> tag works.

Form composition

Because cropping is a form-time action, group image fields into a dedicated Image-titled Panel or Tab. This keeps the crop modal reachable without scrolling past unrelated fields and avoids the common UX complaint that the crop control is easy to miss.

Validation strategy

Layer validation in this order:

  1. Laravel rules on the field (image, mimes:jpg,png,webp, max:5120) — reject non-images early.
  2. dimensions:min_width=1200,min_height=675 — prevent upscaling blur before the crop UI has a chance to.
  3. A model-level saving observer if you need to guarantee a non-null value.

Don't rely on the crop UI alone to enforce minimum size; the API and any programmatic updates bypass it.

Gotchas and Tips

Publish assets in every environment, and re-publish on upgrade

This is the single most common failure. Deploy to staging without running vendor:publish --tag="nova-advanced-image-assets" (or nova:publish) and the crop modal won't appear. Add it to your deploy script alongside nova:assets. On package upgrade, re-publish; the JS is versioned with the PHP.

Old files are probably not cleaned up

Small image-field packages almost always store the new file and leave the old one on disk. If storage cost or GDPR matters, add a saved hook on the model to delete the previous path:

static::updated(function ($model) {
    $old = $model->getOriginal('hero');
    if ($old && $old !== $model->hero) {
        Storage::disk('public')->delete($old);
    }
});

Verify against the package source whether it already handles this before adding redundant logic.

Disk and URL consistency

Set ->disk('public') explicitly and keep it identical across all image fields in the resource. Mixing disks (default vs public vs S3) is the usual cause of "the crop saves but the image 404s." If you're on S3, confirm the package's crop output is written through the same Storage facade rather than move_uploaded_file-style direct writes — small packages sometimes hardcode a local path.

Crop UI is client-side; the server trusts what it gets

The crop coordinates come from the browser. If you expose this field on a public-facing form (unusual for Nova, but possible via custom tooling), treat the output as untrusted and re-validate dimensions server-side. Within Nova's authenticated panel this is a non-issue, but worth noting during code review.

Migrating from the native Image field

Existing records will render fine as long as the column and disk match — the crop UI simply shows the current image uncropped. But the first save on an old record may trigger a re-process/re-encode, changing the file path. Test on a copy of production data before rolling out, and log the old→new path mapping so you can redirect or restore if needed.

Extension points are thin — plan for a fork

With zero dependents and a recent-but-single-maintainer release, there is no ecosystem of extensions. If you need behaviour the field class doesn't expose (custom crop presets, responsive variants, per-locale images), budget for either a thin wrapper Field that extends the package class or a local fork. Read the source first — the resolveUsing/fillUsing hooks are usually public and enough to inject custom logic without forking.

Debugging blank crops

If the modal appears but the saved image isn't cropped, check in this order: (1) assets published and nova:assets rebuilt, (2) php artisan optimize:clear, (3) browser console for a crop-library init error, (4) the network payload — some versions post crop data only when the user drags, so a "save without touching the modal" leaves the original. Reproduce with an explicit drag before filing a bug.

Pin the version and watch release cadence

The last release is recent (Jan 2026) but stars and dependents are near zero. Pin an exact minor version in composer.json, and add a smoke test that uploads+crops a fixture image so a future package bump that changes the asset pipeline fails CI rather than production.

License and support expectations

MIT means you can fork freely — treat that as your support plan. For a low-traffic CMS it's a fine choice; for anything where a broken crop blocks editors, keep the wrapper thin enough that swapping back to vanilla Nova\Fields\Image (and a manual crop step) is a one-line change.

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