naoray/nova-json
A Laravel Nova field for displaying and editing JSON data directly in your resources. It provides a clean, structured interface for working with JSON columns, making nested configuration, metadata, and other arbitrary data easy to manage in Nova.
Install the package:
composer require naoray/nova-json
On Laravel 12 / Nova 5 / PHP 8.4 you need v4.1.0 or later. Earlier 4.x tags carry the old laravel/nova constraint and will fail dependency resolution against Nova 5.
The package is a Nova field, not a service provider with publishable config or migrations. There is nothing to publish — installing and autoloading is enough.
Add the field in a resource's fields() method:
use Naoray\NovaJson\JSON;
public function fields(NovaRequest $request)
{
return [
// ...
JSON::make('Meta'),
];
}
Make sure the backing column exists, and the Eloquent cast matches what the editor expects:
protected $casts = [
'meta' => 'array', // or 'json', or AsArrayObject::class
];
First use case: free-form settings/meta blobs, feature flags, webhook payloads, or cached third-party API responses that don't justify their own relational schema. The field gives you a formatted JSON editor in the Nova form without writing custom Vue.
If the field renders blank after install, confirm the version with composer show naoray/nova-json, hard-refresh the Nova assets, and check that the resource's field name matches the model attribute exactly.
Array cast + JSON rule is the canonical setup. Cast the column to array, bind the field, and validate at the resource level with Laravel's built-in json rule:
JSON::make('Meta')
->rules('json')
->nullable(),
Read-only audit views. For request/response logs or anything you never want edited by hand:
JSON::make('Payload')
->onlyOnDetail()
->readonly(),
Hide from index. JSON blobs bloated into index tables destroy Nova's list performance and readability. Use ->hideFromIndex() and show the field only in the form and detail views.
Stable shapes for API consumers. Because the model cast re-encodes the array on every save, whitespace and key ordering normalize automatically. That's usually desirable; if a downstream consumer needs byte-exact preservation, don't cast — store the raw string and use a plain Textarea-style field instead.
Consistent editor defaults. If you want the same options (height, theme, read-only behaviour) across many resources, subclass the field once:
namespace App\Nova\Fields;
class JsonEditor extends \Naoray\NovaJson\JSON
{
public function __construct($name, $attribute = null, $resolveCallback = null)
{
parent::__construct($name, $attribute, $resolveCallback);
$this->rules('json');
}
}
Then use JsonEditor::make('Meta') everywhere.
API-driven writes. When values are written from jobs or API controllers, write the decoded array ($model->meta = $payload;) rather than a pre-encoded string — the cast handles encoding consistently and you avoid double-encoding bugs.
Schema validation belongs to you. The field is an editor, not a validator. If the payload has a known shape, attach a custom rule (->rules(new JsonSchemaRule)) alongside json; the package will not enforce structure for you.
Nova 5 asset pipeline. Nova 5 switched to a Vite-era asset manifest. After upgrading from Nova 4, clear cached assets (php artisan nova:publish --force, php artisan view:clear) if the field appears as an empty box or throws a missing-component error. The package code is compatible; stale compiled assets are the usual culprit.
Version pinning during the Nova 4 → 5 transition. If you run multiple apps, pin naoray/nova-json:^4.1 explicitly. A loose ^4.0 on a Nova 5 app can resolve to an older tag on a cold cache and produce confusing install failures.
Cast mismatch is the most common failure mode. If the model doesn't cast the attribute, the editor shows a quoted, escaped string. If the column is a plain string but you cast to array, writes fail with an array-to-string conversion. Diagnose with:
dd($model->getRawOriginal('meta'), $model->meta);
Empty string vs null. An empty textarea submits '', which does not cast to a useful array and can slip past the json rule in some configurations. Add ->nullable() and normalize empty values in a mutator or booted() hook.
MySQL JSON columns reorder keys and strip whitespace. Don't write byte-level diff assertions in tests; decode and compare arrays instead.
Large payloads hurt the editor. The whole value is loaded into a browser-side editor. Multi-hundred-KB blobs make the form sluggish — consider a read-only detail view or a modal for large documents.
Standard Nova field API applies. resolveUsing, fillUsing, displayUsing, default, dependsOn, and readonly all behave as they would on any Nova field. There is no package-specific extension API to learn, which makes the field easy to swap out later if requirements change.
Test at the cast layer, not the UI layer. Unit-test the JSON round-trip ($model->meta = [...] → save → reload) independently of Nova. Spot-check the rendered detail view with assertSee for the pretty-printed payload rather than driving the editor in every feature test.
How can I help you explore Laravel packages today?