sebastiaankloos/filament-code-editor
Filament Code Editor adds a ready-to-use code editor form field to Filament panels, with syntax highlighting, line numbers, and configurable options for editing code and JSON directly in your admin forms.
The package ships a single Filament form component that wraps an embedded code editor (CodeMirror-based). You drop it into a form schema like any other field.
1. Install and register assets
composer require sebastiaankloos/filament-code-editor
php artisan vendor:publish --tag="filament-code-editor-assets" # if the tag exists in your version
2. Confirm the compatibility matrix before you write code. This package's last release was April 2023, which predates Filament v3. Open vendor/sebastiaankloos/filament-code-editor/composer.json and check the filament/filament constraint. If it caps at ^2.0, either pin your panel to Filament v2, or budget time to fork and port the field (the component is small — one PHP field class, a Blade view, and a JS asset). Do this check first; almost everything else in this assessment assumes the field loads.
3. Find the entry point. Read these three files in order:
src/Components/CodeEditor.php (or whatever the class namespace is — grep for extends Field) — this is the public API surface: language(), height(), theme(), readOnly().resources/views/*.blade.php — shows how the editor is mounted and which JS/Alpine hooks are used.resources/js/* — shows the editor library version and how state is synced back to Livewire.4. First use case: a settings/config editor. The highest-value, lowest-risk integration is editing a stored text value that is not PHP:
use SebastiaanKloos\FilamentCodeEditor\Components\CodeEditor;
CodeEditor::make('value')
->label('JSON configuration')
->language('json')
->height('420px')
->required(),
Put it on a Settings custom page or a SiteConfig resource, back it with a longText column, and you have a working admin-side config editor in ~10 minutes. Start here rather than on a PHP/Blade template field — you get the full editing experience without exposing arbitrary code execution paths on day one.
Form schema field, resource or custom page. The field is schema-scoped, so it works identically in Resource::form(), ->form() on a page, and inside Filament\Forms\Components\Section/Grid:
public static function form(Form $form): Form
{
return $form->schema([
Section::make('Template')
->schema([
CodeEditor::make('body')
->language('blade')
->height('60vh')
->columnSpanFull(),
]),
]);
}
Language selection driven by another field. The most common real-world pattern is a Select for the file/format type plus a code field whose language follows it. Use ->reactive() on the select and ->language(fn (Closure $get) => $get('format')). If the language prop is applied only at mount time in your version, use ->key(fn ($get) => 'editor-'.$get('format')) to force a remount — cheap and reliable.
Validation is your job, not the editor's. Syntax highlighting is cosmetic; the field will happily save broken JSON. Attach a rule:
CodeEditor::make('config')
->language('json')
->rules([
fn () => function (string $attribute, $value, Closure $fail) {
json_decode($value);
if (json_last_error() !== JSON_ERROR_NONE) {
$fail('Invalid JSON: '.json_last_error_msg());
}
},
]),
For CSS/JS/Blade, at minimum run a server-side sanity check (balanced braces, no <?php in a non-PHP field) rather than trusting the editor.
Read-only viewer pattern. Use the same field with ->readOnly() and ->height('70vh') inside a ViewRecord page or a modal action to show config snapshots or diffs. Cheaper than building a separate syntax-highlighted renderer, and visually consistent with the editor.
Template editing with a safety net. If you let admins edit Blade/PHP, store the content in the DB and compile it to a cache file or Blade::string() at render time — never write it into resources/views from a web request. Pair the field with a "preview" action and an audit column (updated_by, updated_at) so a bad save is recoverable. Git-backed storage (write to a repo, commit on save) is the pattern that survives contact with production.
Repeater / modal caveat handling. When the field lives inside a Repeater or modal, mount it in an Alpine x-init on the container rather than at page load, and destroy on removal — otherwise you get overlapping editor DOM for each row. If you only need N=1 editors per page, keep it that way; this package is not optimized for heavy repeater use.
It is archived. No security patches, no Filament v3/v4 support, no fixes for editor-library CVEs. Treat it as a vendored component: fork it into packages/, or copy the field class into your app. Add a note in your repo so future-you knows why the dependency is pinned.
Filament version mismatch is the #1 failure. Symptoms: field renders as a plain textarea, or JS throws Alpine is not defined / editor never initializes. Verify the constraint in composer.json and check that the asset registration (FilamentAsset::register / Alpine::register) runs on the Filament version you have.
Assets may load from a CDN. Many small editor packages pull the CodeMirror/Monaco bundle from a CDN. On air-gapped or CSP-strict environments the editor silently degrades. Check the Blade view for https:// references and publish/vendor the JS locally if so, then update your CSP script-src.
Livewire sync and debounce. The editor updates state via wire:model/$wire.set. Large files (hundreds of KB) will produce laggy typing if it syncs on every keystroke. Wrap with ->defer() / ->debounce(750) on the underlying state, and keep a maxlength or explicit size check — Livewire payload limits will bite before you expect.
Dark mode. Editor themes are separate from Filament's dark mode. Pass a theme explicitly (e.g. a light theme for light panels) and, if your version supports it, bind it reactively to the panel's appearance. Otherwise you get a white editor in a dark panel, which is the most common cosmetic complaint.
Height units. height() may accept px only in some versions; vh/rem may be ignored or break the layout inside Section/Grid. Test with ->columnSpanFull() and set a fixed pixel height if percentages collapse.
Validation messages are not surfaced by the editor. Because the editor replaces the <input>, the standard Filament error state may not visually appear on the field. Confirm @error handling in the Blade view; if missing, add a visible error block or wrap the field so users see why saving failed.
Extension points, ranked by cost. 1) Field props (language, height, theme, readOnly) — free. 2) Publish and edit the Blade view to add a "format/pretty-print" button — cheap and high value for JSON. 3) Publish the JS to add keybindings or a custom theme — moderate. 4) Add a new language mode — requires registering it in the editor instance; check whether the package exposes a config array for modes before hacking the JS.
Debugging checklist when the field misbehaves: is the asset rendered (<script>/<link> in the page source)? is the Alpine component present in the DOM (x-data="..." on the wrapper)? does the console show an editor initialization error? is the Livewire property name correct? Answer those four before touching the package source.
Prefer a maintained alternative if you're starting fresh. If your only need is JSON/config editing, a plain Textarea with ->rows() plus a JSON validation rule covers 80% of the value at zero maintenance cost. Reach for this package when the syntax highlighting and line numbers genuinely improve the workflow — and accept that you're adopting it as a fork, not a dependency.
How can I help you explore Laravel packages today?