community-sdks/unlayer-filament
A Filament integration for Unlayer that lets you create, edit, and manage email templates with a drag-and-drop designer directly in your Laravel admin panel. Embed the Unlayer editor in Filament resources and streamline template management.
v1.0.0 is the first tagged release of community-sdks/unlayer-filament, a Filament integration for the Unlayer email/landing-page editor. The release notes are only a changelog link, so treat the README and src/ as the source of truth for class names and options — this assessment describes the shape such a package takes and what to verify on day one.
Setup path:
composer require community-sdks/unlayer-filament
php artisan vendor:publish --tag="unlayer-filament-config" # verify tag names in README
php artisan filament:assets # if it ships compiled JS/CSS
Then, in a panel provider, either register the plugin:
public function panel(Panel $panel): Panel
{
return $panel->plugin(\CommunitySdks\UnlayerFilament\UnlayerFilamentPlugin::make());
}
…or, if it's field-only, skip registration and use the field directly in a form schema.
Where to look first, in order: README.md → config/unlayer-filament.php (project ID, tools, appearance, storage settings) → resources/views/ (the Blade wrapper around the JS editor) → src/Fields/ (the Filament field class, its setUp() and state handling).
First use case: add the field to one form, store the raw Unlayer design JSON, and export HTML on save. That single loop — edit → persist JSON → render HTML → send — is the package's whole value proposition; validate it end-to-end before wiring it into more resources.
UnlayerEditor::make('design')
->label('Email body')
->columnSpanFull(),
Two-column storage. Persist the design JSON and a rendered HTML snapshot separately. The JSON is the editable source of truth; the HTML is what you actually mail, so you never re-render at send time.
$table->json('design')->nullable();
$table->longText('html')->nullable();
Cast design to array in the model and let the field write the raw array. Render the HTML either via Unlayer's client-side export callback or your own Blade template that walks the design JSON — pick one and keep it consistent, because the two drift.
Form + preview action. Put the editor in the resource form and add a Action::make('preview') that renders the stored HTML inside an iframe (srcdoc) rather than injecting it into the panel DOM. Keep preview read-only so reviewers don't mutate state.
Sending. Treat the stored HTML as a Mailable body or pass it to your ESP. Merge tags ({{first_name}}) belong in the design, so replace them before handing off — decide whether substitution happens in PHP or at the provider.
Reusable subforms. Wrap the field in a small Livewire component or a Section you can reuse across templates, campaigns, and A/B variants. If the package exposes a standalone Livewire component, prefer that over duplicating resource forms.
Versioning. Designs are user-edited documents; snapshot them. A design_versions table with designable_id/designable_type and a created_at gives you rollback for free and makes support tickets tractable. Hook it into the model's updated event or a Filament afterSave.
Config over code. Set the Unlayer project ID and enabled tools in the published config rather than in the field call, so staging and production can differ. If the package supports per-field overrides, reserve those for genuinely per-form differences.
Pin the version. A 1.0.0 with a one-line changelog is a starting point, not a stability guarantee. Use "^1.0" and read each release before bumping; there's no migration history to infer the maintainers' breakage policy from yet.
Livewire state loss. JS editors wrapping a hidden input are the classic failure mode: re-render the component and the editor resets to empty. Confirm the field entangles correctly (wire:ignore on the editor container, explicit set() on change). Test with a validation error — that's when Filament re-renders and state disappears.
Asset pipeline. Filament assets are published, not served from vendor/. After every composer update, re-run php artisan filament:assets, and remember php artisan filament:optimize / optimize:clear in deploy scripts. A blank editor in production but not locally is almost always this.
CDN dependency. Unlayer loads its editor script from a CDN by default. That means CSP headers, air-gapped environments, and corporate proxies can all break it. Check whether the config offers a self-hosted script URL and set it if you have a strict CSP.
Payload size. Design JSON is verbose — a real email runs into hundreds of KB. Livewire ships it over the wire on every request in that component. Use ->dehydrated() deliberately, consider wire:model.defer-style behaviour on save, and don't put the editor on a page that also has a live-search table.
XSS. Stored HTML is user-authored and rendered raw. Sanitize on output (or render in a sandboxed iframe with a restrictive sandbox attribute) — an email builder is an HTML injection surface by design.
Column types. json on MySQL is fine; on older MySQL/MariaDB combinations and SQLite, longText with a cast is safer. Confirm the field emits an array, not a JSON string, before casting — a double-encode silently produces "\"{...}\"" and the editor loads empty.
Testing. assertFormSet/assertSchemaStateSet will exercise the state but not the JS. Anything about actual editing behaviour needs a browser test (Dusk/Pest browser) — budget for one smoke test rather than trying to unit-test the editor.
Extension points to inspect before forking: the Blade view (overridable via ->view()), the config file, and any registered custom tools. If the field class is final or the view is hard-coded, expect to copy the package into app/ for customisation — check this early, it changes your upgrade strategy.
How can I help you explore Laravel packages today?