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

Filament Jsoneditor Laravel Package

invaders-xx/filament-jsoneditor

A Filament form field that adds a full-featured JSON editor to your Laravel admin panels. Edit, validate, and format JSON directly inside Filament forms with syntax highlighting, tree view, and a smooth, developer-friendly editing experience.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package alongside a Filament v3 panel:

composer require invaders-xx/filament-jsoneditor
php artisan filament:assets      # or: php artisan vendor:publish --tag=filament-jsoneditor-assets

Check the version line first. The current release is 5.0, whose only shipped change is Laravel 12 support (PR #41). That means 5.x is a framework-constraint release, not a feature release: if you're on Laravel 10/11, you stay on 4.2.1; if you're on Laravel 12 you need 5.x. Nothing in the field's fluent API changed between 4.2.1 and 5.0 as far as the changelog shows — so anything you learn about the component applies to both lines.

The field is a regular Filament form component, so it inherits everything you already know (label(), helperText(), required(), columnSpan(), hidden(), afterStateUpdated(), dehydrateStateUsing(), etc.). The only new thing is the visual editor, which wraps the jsoneditor JS library in an Alpine component bound to the Livewire state.

First real use case — a raw JSON column on a model:

// migration
$table->json('payload')->nullable();

// resource
use InvadersXX\FilamentJsoneditor\Forms\JSONEditor;

JSONEditor::make('payload')
    ->label('Webhook payload')
    ->columnSpanFull()
    ->rules(['nullable', 'json']);

Where to look first:

  1. The render hook / Blade view shipped by the package (usually resources/views/…) — it shows the exact Alpine wiring, the container height, and how the value is passed in/out. That file is also your override point.
  2. The field class itself — it lists every editor option exposed as a fluent method (mode(), modes(), search(), navigationBar(), statusBar(), menuBar(), onChange(), …). Note this API is identical on 4.2.1 and 5.0.
  3. Your own model cast/mutator, because that's where 90 % of the integration bugs come from (see below).

Implementation Patterns

State handling (do this one thing right). The editor speaks strings; Eloquent's array/json cast speaks arrays. Pick one contract and stick to it.

// Option A — store a plain JSON string, no cast on the model
JSONEditor::make('payload')
    ->rules(['nullable', 'json']);

// Option B — keep the `array` cast and convert at the field boundary
JSONEditor::make('payload')
    ->formatStateUsing(fn ($state) => is_array($state)
        ? json_encode($state, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
        : $state)
    ->dehydrateStateUsing(fn ($state) => blank($state) ? null : json_decode($state, true))
    ->rules(['nullable', 'json']);

Option B is the one I'd default to: the rest of the app keeps working with arrays while the admin edits pretty-printed JSON. Because 5.0 changed nothing at the state boundary, this pattern carries over from 4.x unchanged.

Read-only / audit views. ->disabled() alone doesn't reliably stop a JS editor from accepting keystrokes. Use the editor's own read-only mode instead, and drop the bar chrome so it looks like a viewer:

JSONEditor::make('payload')
    ->mode('view')
    ->modes(['view'])
    ->menuBar(false)
    ->navigationBar(false)
    ->statusBar(false)
    ->columnSpanFull();

Tree mode is the differentiator. If your audience is non-developers editing nested config (feature flags, SEO schema, shipping rules), start in tree and allow code as a secondary mode:

JSONEditor::make('settings')
    ->modes(['tree', 'code', 'form'])
    ->mode('tree')
    ->search(false)
    ->columnSpanFull();

For developer-only raw payloads, skip the UI affordances and go straight to code mode with history enabled.

Settings / custom pages. Inside a custom Filament page with a form, treat it exactly like any other field — the page's form->fill() / getFormState() flow works unchanged. Same for relation managers and modals, provided the editor lives in a single field (not a repeater item — see Gotchas).

Table listing. The package is a form component only. For the index table, don't render the editor; show a summary column:

TextColumn::make('payload')
    ->formatStateUsing(fn ($state) => is_array($state)
        ? json_encode($state, JSON_UNESCAPED_SLASHES)
        : (string) $state)
    ->limit(60)
    ->wrap()
    ->tooltip(fn ($state) => Str::limit((string) json_encode($state), 500)),

Validation beyond json. The json rule only checks syntax. Layer shape checks with a closure or a dedicated rule so a syntactically valid but semantically wrong document doesn't reach production:

->rules([
    'nullable',
    'json',
    function (string $attribute, $value, Closure $fail) {
        $decoded = json_decode($value, true);
        if (! is_array($decoded) || ! array_key_exists('url', $decoded)) {
            $fail('Payload must contain a "url" key.');
        }
    },
])

Testing. Livewire/Filament tests don't need a browser — fill the field with a JSON string:

livewire(EditWebhook::class, ['record' => $webhook->id])
    ->fillForm(['payload' => json_encode(['url' => 'https://example.test'])])
    ->call('save')
    ->assertHasNoFormErrors();

Audit trail. Pair it with a afterStateUpdated hook to diff old vs. new JSON into an activity log — config edits are exactly the kind of change reviewers want history on.

Gotchas and Tips

  • Version line ↔ framework matrix. 5.0 exists only to add Laravel 12 support, and it's a major bump — read that as "composer constraints changed," not "features changed." Expect 5.x to require Laravel 12 (and therefore PHP 8.2+), and possibly to drop earlier Illuminate versions. Check composer.json before upgrading, and pin explicitly: "invaders-xx/filament-jsoneditor": "^5.0" on Laravel 12, "^4.2" otherwise. Don't let a floating constraint drag you across the boundary by accident.
  • Verify the class name before you ship. Small packages sometimes rename or re-case the field (JsonEditor vs JSONEditor, Forms\… vs Components\…). Run composer show invaders-xx/filament-jsoneditor and read the source tree once, then standardise across your codebase. Do this again after the 5.0 bump — even a constraints-only major can carry incidental cleanups.
  • Filament v4 is still unconfirmed. The 5.0 changelog mentions Laravel 12 only — no Filament v4 support statement. Laravel 12 and Filament v4 are different tracks; don't infer one from the other. If you're on Filament v4, verify against a dev branch or budget time to fork/vendor the field, and pin "filament/filament" in lockstep with the plugin.
  • Asset source matters. Confirm whether the jsoneditor JS/CSS is served from a CDN or published locally. If it's a CDN, air-gapped and corporate-proxy environments will render an empty box with no visible error — a classic "works locally, broken in staging" failure. Publish/host the assets locally if you can, and re-run filament:assets after the upgrade in case the published set changed.
  • Height and layout. The editor needs an explicit height; the default is typically a few hundred pixels. It does not auto-grow, and it doesn't play well with columnSpan(1) in a grid — use columnSpanFull() and, if you need more room, override the package's view or add panel CSS targeting the container rather than hacking inline styles.
  • live() on large documents is a request storm. The editor pushes a change event per keystroke burst. Leave the binding deferred (default) for big payloads; reserve ->live(debounce: 800) for small documents where you genuinely need reactive dependent fields.
  • Repeaters and dynamic nesting. Alpine-based fields frequently lose their instance when a repeater row is added/removed or reordered. Test it explicitly; if it breaks, fall back to a plain Textarea with ->rows(20) inside repeaters and keep the JSON editor for top-level fields.
  • Dark mode. Custom JS editors are the usual suspect for white-background flashes in dark panels. Check both themes; if needed, add CSS variables via a custom theme rather than a vendor edit.
  • Empty vs. "null". Clearing the editor can leave the string "null" or "" rather than PHP null. Normalise in dehydrateStateUsing() (blank($state) ? null : …) so nullable columns stay genuinely nullable.
  • Extension point. When a method you need isn't exposed, don't fork: the fluent API is thin, and the JS options are usually passed through a single config array. Adding ->jsonEditorOptions([...])-style passthrough (or overriding the Blade view via vendor:publish) is a small, upgrade-friendly patch.
  • Maintenance risk is unchanged, and the release history tells you why. A major version number earned entirely by a framework-constraint bump, a single maintainer, and a thin dependent graph means the project tracks Laravel releases reactively rather than driving features. That's fine for a field this small — but wrap it in your own thin App\Forms\JsonEditor subclass (or a Blade component) so a future swap to a CodeMirror-based field is a one-file change, and keep the version pinned in composer.json.
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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package