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 Ace Editor Laravel Package

open-kampus/filament-ace-editor

Ace Editor field for Filament forms, adding a full-featured code editor with syntax highlighting and themes to your admin panels. Edit JSON, HTML, CSS, PHP and more directly in Filament resources, with configurable editor options and a simple, drop-in form component.

View on GitHub
Deep Wiki
Context7

Getting Started

open-kampus/filament-ace-editor adds an Ace code editor form component to Filament panels — the fastest way to replace a plain Textarea when users edit JSON, CSS, JS, HTML, Blade, SQL, or YAML inside an admin resource.

  1. Install

    composer require open-kampus/filament-ace-editor
    

    Version 1.1.2 adds Laravel 12 support, so it's a safe bump if you're moving a Filament v3 panel onto a Laravel 12 host. It is a compatibility release — no new API surface.

  2. Publish Filament assets (the JS/CSS is registered through Filament's asset pipeline, not auto-injected):

    php artisan filament:assets
    php artisan optimize:clear
    
  3. First use case — a settings/resource form where a content column holds raw CSS or JSON:

    use OpenKampus\FilamentAceEditor\Forms\Components\AceEditor;
    
    AceEditor::make('content')
        ->mode('css')
        ->theme('monokai')
        ->height('420px')
        ->columnSpanFull();
    

Where to look first:

  • The README's method table (the fluent API is small: mode(), theme(), plus an options passthrough — confirm exact names against the installed version).
  • src/Forms/Components/AceEditor.php — all available setters and their defaults.
  • The package Blade view — this is where your PHP options are serialized into the JS ace.edit() init call, and it's the file you'll touch if you need behavior the fluent API doesn't expose.

Implementation Patterns

Keep the mode/theme per field, not global. A single panel often needs several: json for config blobs, php_laravel_blade for templates, css for theming, sql for report queries. Set them inline so the intent is readable at the call site.

Make the database value and the editor value agree. Ace gives you a raw string; your column may be JSON or an array cast. Normalize both directions:

AceEditor::make('payload')
    ->mode('json')
    ->formatStateUsing(fn ($state) => is_array($state)
        ? json_encode($state, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
        : (string) $state)
    ->dehydrateStateUsing(fn ($state) => filled($state) ? json_decode($state, true) : null)
    ->rules(['nullable', 'json']);

The json rule plus the dehydrate hook is usually enough; you don't need a custom validator, but do validate — Ace will happily let a user save a trailing comma.

Use it as a read-only viewer on View pages. Rather than duplicating the field, gate it:

AceEditor::make('content')
    ->mode('html')
    ->disabled(fn (string $operation) => $operation === 'view');

Combine with ->columnSpanFull() and an explicit ->height(), otherwise the editor collapses into a short box inside a two-column grid. Disabled state is also how you avoid the editor swallowing clicks meant for the surrounding form.

Be deliberate about reactivity. The editor is a JS widget syncing into Livewire state. ->live() on every keystroke will round-trip the whole component per character and can cause cursor jumps or re-init flicker. Prefer ->live(onBlur: true) or ->live(debounce: 800) when you need dependent fields (e.g. a live JSON preview). If you only need the value on submit, don't make it live at all.

Repeaters and Builders work, but verify re-init. When a repeater row is added, the editor container is re-rendered; the package's Alpine hook is responsible for re-mounting Ace on the new node. Test adding 2–3 rows and editing in each — this is the most common place a field-wrapper package breaks, and it's cheap to check before shipping.

Testing: your Pest/PHPUnit form tests never execute Ace. Call ->fillForm(['content' => '...'])->call('save') and assert on the persisted value and validation errors only. Don't assert on rendered editor markup — it's brittle and tells you nothing.

Gotchas and Tips

  • Run filament:assets after every composer update. Ace's JS won't load otherwise and you'll get an empty grey box with no console error from Laravel's side. Same fix for the classic "editor rendered fine yesterday" after a deploy that skipped asset publishing.
  • Check where Ace itself comes from. Confirm whether the package pulls ace-builds locally or references a CDN. If you're deploying to an air-gapped or CSP-restricted environment, this matters — Ace's workers and some modes use blob/eval-like constructs that a strict script-src policy will block. Test in the target environment, not just locally.
  • Stale view cache after upgrades. php artisan view:clear resolves most "my options aren't taking effect" reports; the options are baked into the view at render time.
  • Dark mode isn't automatic. The theme() is fixed — it won't follow the panel's dark toggle. Either pick a theme that reads acceptably in both, or bind it: ->theme(fn () => filament()->getCurrentPanel()->hasDarkModeForced() ? 'monokai' : 'chrome'). Also keep the editor background consistent with your panel or the field will look pasted-in.
  • Empty string vs. null. Ace initializes to '' for null state. Pair formatStateUsing with filled() checks if the column is nullable and you have a json rule, otherwise an untouched required-looking field fails validation on save.
  • Large files will hurt. Ace is fine into the hundreds of KB, but Livewire syncing multi-megabyte blobs per keystroke is where you'll see the panel stall. Cap input with ->maxLength() or move genuinely large content to a dedicated page.
  • Extension points, in order of preference: (1) the options passthrough array for one-off Ace settings; (2) subclass AceEditor and override the view for a reusable preset (e.g. a BladeEditor with mode + theme + height pre-baked); (3) fork the Blade view if you need custom JS events (ace.edit() callbacks, on('change') hooks tied to Livewire). Subclassing is the cleanest and survives package updates far better than overriding the view.
  • On 1.1.2 specifically: the only change is Laravel 12 support. No breaking changes, no deprecations, no new options. If you're on Laravel 11 there is no reason to rush the upgrade; if you're migrating to Laravel 12, this is the version that unblocks you. If you're on Filament v4, confirm compatibility separately — the release note speaks to the Laravel constraint, not the Filament one.
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
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager