parallax/filament-syntax-entry
A Filament form field that adds a syntax-highlighted code editor to your admin panels. SyntaxEntry makes editing code, JSON, and config in forms easier with configurable languages, themes, and a clean developer-friendly input experience.
This is a read-only infolist entry, so the mental model is: render code on a "view" page (resource view, relation manager, or a custom infolist page). It is not a form field, not a table column, and it does not let users edit code.
Install
composer require parallax/filament-syntax-entry
Check the README for the exact version constraint against your Filament major (v3 vs v4). With 18 stars and a single maintainer, pin the tag rather than floating ^1.0.
Assets/config Most Filament asset packages either auto-register a service provider or expose a publish tag. Look for one of:
php artisan vendor:publish --tag="filament-syntax-entry-config"
php artisan vendor:publish --tag="filament-syntax-entry-assets"
If a plugin class is shipped (Filament v4 style), register it in your panel provider:
->plugin(FilamentSyntaxEntryPlugin::make())
First use case — drop it into an existing Resource::infolist() (or a ViewRecord page's schema) to show a stored payload such as a webhook body, a JSON config blob, or a stored SQL migration:
use Parallax\FilamentSyntaxEntry\Infolists\Components\SyntaxEntry;
public static function infolist(Infolist $infolist): Infolist
{
return $infolist->schema([
SyntaxEntry::make('payload')
->language('json'),
]);
}
Look here first when something is off: the package's resources/views for how it emits the <pre><code> block, and resources/js for which highlight.js build/plugins it loads (core vs. common languages, line numbers, etc.).
Explicit language beats auto-detection. highlight.js's auto-detect is slow and frequently wrong on short snippets. Always pass ->language('php'), ->language('json'), etc., and derive it from the record when it varies:
SyntaxEntry::make('source')
->language(fn ($record) => $record->file_extension === 'php' ? 'php' : 'plaintext')
Theme per-entry vs. global. Themes (github-dark, atom-one-dark, monokai, …) are highlight.js stylesheet names. Set a sane default in config and override only where a view needs it:
SyntaxEntry::make('diff')->theme('github-dark')->language('diff')
Compose inside infolist layout primitives. It is an ordinary entry, so it nests in Section, Grid, and Split alongside TextEntrys. A common pattern is a metadata section (author, size, updated_at) followed by the syntax entry spanning full width.
Reuse via a named wrapper. If you render the same kind of payload in several resources, extract it:
class JsonPayloadEntry extends SyntaxEntry
{
protected function setUp(): void
{
parent::setUp();
$this->language('json')->theme('github-dark');
}
}
Then use JsonPayloadEntry::make('payload'). This also gives you one place to react to upstream API changes.
Feed it computed content, not just raw columns — a diff, a normalized JSON dump, or a generated snippet:
SyntaxEntry::make('payload')
->state(fn ($record) => json_encode($record->payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES))
->language('json')
On custom pages, build an Infolist the same way you would for a resource; nothing about the package is resource-specific.
Test the schema, not the JS. Feature tests can assert the entry is present and configured (assertSeeLivewire, schema assertComponentExists), but highlight.js rendering happens in the browser — cover it manually or with a Dusk test that asserts the hljs class is applied.
js, ts, bash, sh, yml, json, php, html). An unregistered name silently produces unhighlighted output — no exception. If a snippet is blank of color, suspect the language string first.->theme() just falls back to unstyled (or the default). Verify against highlight.js's styles/ directory.github (light) theme will look wrong on Filament's dark panel. Either configure a dark-friendly theme globally, or scope two stylesheets and toggle by Filament's dark class in your own CSS.hljs.highlightAll() on the relevant DOM node after update.str($record->payload)->limit(20000)) or paginate the content.php artisan config:cache in production will serve the cached value — a config edit in a deploy script needs a cache clear.composer.json.<code> element — inspect the DOM; (4) is the content actually populated on the entry (->state() returning null is a common cause of an empty block that looks like a broken highlighter).How can I help you explore Laravel packages today?