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

bbs-lab/nova-cloudinary-field

bbs-lab/nova-cloudinary-field provides a Laravel Nova field for seamless Cloudinary integration, letting you upload, preview, and manage images and media directly within Nova resources using Cloudinary's cloud storage and transformation capabilities.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the field into an existing Nova application:

composer require bbs-lab/nova-cloudinary-field
php artisan vendor:publish --tag=cloudinary-field-config   # if a config tag is offered

Add your Cloudinary credentials to .env before touching any resource class — the field resolves credentials at render time, so a missing key shows up as a broken preview rather than an exception:

CLOUDINARY_URL=cloudinary://<api_key>:<api_secret>@<cloud_name>
# or the discrete form
CLOUDINARY_CLOUD_NAME=
CLOUDINARY_API_KEY=
CLOUDINARY_API_SECRET=

Where to look first, in order:

  1. config/cloudinary.php (or the package's own config) — confirms which env keys are actually read.
  2. The field class itself (src/*Field.php) — check the component() name so you know which Vue component renders in Nova, and read resolve()/fill() to see exactly what is stored in the database column.
  3. An existing Nova resource — add the field to one throwaway resource before rolling it out.

First use case: a cover_image column on a Post resource.

use BbsLab\NovaCloudinaryField\CloudinaryImage; // verify exact FQCN in src/

public function fields(NovaRequest $request)
{
    return [
        CloudinaryImage::make('Cover Image', 'cover_image')
            ->rules('nullable'),
    ];
}

Upload one image, save, then inspect the row with php artisan tinker — this single check tells you whether the column holds a Cloudinary public_id or a full delivery URL, which determines everything downstream.

Implementation Patterns

Store the public ID, derive the URL. The field's main value is separating persistence from delivery. Persist the public_id (or whatever the field resolves) and build URLs at render time so you can change transformations without a data migration:

// app/Models/Post.php
public function coverUrl(int $width = 1200): ?string
{
    if (! $this->cover_image) {
        return null;
    }

    return cloudinary()->image($this->cover_image)
        ->resize(Resize::scale()->width($width))
        ->toUrl();
}

One field instance per column, transformations in the field config. Keep the Nova-side config focused on editor concerns (aspect ratio, crop mode, max upload size) and keep delivery concerns (responsive srcset, format negotiation f_auto,q_auto) in your application layer. Mixing them means every transformation tweak requires a Nova deploy.

Multiple images / galleries. For a JSON or media table setup, attach the field per-item inside a HasMany or use the field's multiple mode if available; validate array and constrain count with ->rules('array', 'max:8'). Don't rely on the field for cardinality enforcement — it's a UI affordance.

Index and detail views. Nova renders the field component in index/detail contexts too; if you have large galleries, add ->onlyOnForms() or ->exceptOnForms() deliberately so the index table doesn't attempt to load N thumbnails per row.

Integration with existing Cloudinary usage. If you already use cloudinary-labs/cloudinary-laravel, make sure both packages resolve credentials from the same source. Duplicated config (one package reading CLOUDINARY_URL, the other reading CLOUDINARY_CLOUD_NAME) is the most common source of "upload works in Nova but URLs 404" reports.

Testing. Never hit the real Cloudinary API in tests. Use Laravel's Http::fake() for the upload endpoint, or store fixture public IDs directly on factories and only assert on the generated URL shape. For Nova field tests, Nova::assertField() style assertions are enough — you rarely need to exercise the JS component.

Upgrading for Laravel 13. v2.2.0 is a compatibility release: it widens the illuminate/* / laravel/framework constraint to include Laravel 13. There is no field API change, no config change, and no migration. Treat it as a routine patch: composer update bbs-lab/nova-cloudinary-field and re-run your Nova field tests. Confirm your Nova major version supports Laravel 13 before bumping — Nova's own constraint is the real gate, not this package's.

Gotchas and Tips

  • config:cache and env changes. Any credential fix in .env requires php artisan config:cache (or config:clear) in cached environments. A stale config cache is the #1 cause of "credentials are set but uploads fail."
  • Secret leakage via unsigned presets. If you configure an unsigned upload preset to avoid shipping the API secret to the browser, remember that anyone can then upload to your account with that preset. Lock the preset down (allowed formats, max size, folder) and never reuse it across environments.
  • Deleting models does not delete assets. Cloudinary storage is billed and permanent. Register a model observer or deleted event to call cloudinary()->destroy($publicId) — and be careful with soft deletes, where you usually want to keep the asset until force-delete.
  • Orphaned assets on field replacement. When a user swaps an image, the old public_id is silently dropped unless you diff in saving/saved. Capture the original value with $model->getOriginal('cover_image') inside an observer.
  • Transformation cost and cache-busting. Cloudinary caches derived assets by transformation string. A seemingly harmless change (adding q_auto) creates an entirely new derived asset and a cold cache. Standardise on a shared transformation helper rather than ad-hoc strings scattered across views.
  • f_auto on non-image extensions. If the column can hold PDFs or videos (Nova's file fields often do), f_auto on a video public ID with the image endpoint returns a broken URL. Branch on resource type.
  • Local development without credentials. Give the field a graceful fallback rather than a hard error — a null check before generating the URL keeps your resource pages usable for developers who don't have Cloudinary keys.
  • Extension point: the field's resolveUsing / displayUsing callbacks are the cleanest place to customise what the Nova index displays (e.g. a low-res w_200 thumbnail) without forking the Vue component.
  • Version pinning. The v2.x line is the supported track; if you're still on Nova 3 or Laravel 8/9, this release won't help you — stay on the constraint your app already satisfies and don't force the update with --with-dependencies.
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.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route