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

Nova Unlayer Field Laravel Package

idf/nova-unlayer-field

A Laravel Nova field that integrates the Unlayer drag-and-drop email builder, letting you design, edit, and save responsive email templates directly inside Nova resources. Ideal for managing newsletters and transactional emails from your admin panel with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the field and give it somewhere to write:

composer require idf/nova-unlayer-field

Then add the storage columns. A working default is two columns per builder — one for the editable design, one for rendered output:

Schema::table('emails', function (Blueprint $table) {
    $table->json('design')->nullable();      // Unlayer design JSON, for re-editing
    $table->longText('html')->nullable();    // exported HTML, for sending
});

Register the field on your Nova resource:

use IdF\NovaUnlayerField\NovaUnlayerField;

public function fields(Request $request)
{
    return [
        NovaUnlayerField::make('Design', 'design')
            ->displayMode('email')          // 'email' | 'web' | 'popup' | 'document'
            ->hideFromIndex(),
    ];
}

Where to look first, in order:

  1. vendor/idf/nova-unlayer-field/src/ — the field class. Confirm the exact fluent method names (displayMode, mergeTags, withUnlayerOptions, etc.) and which attribute the design JSON is actually posted to; the README is thin and the source is the contract.
  2. resources/js/components/ — the Vue component that loads https://editor.unlayer.com/embed.js. This tells you which Unlayer init() options the field exposes versus which you must pass through as raw JSON.
  3. composer.json / nova constraint — check it supports your Nova major (4 vs 5) before anything else, since the Vue asset pipeline differs.

First use case to prove out: replace a <textarea> of raw HTML on an Email resource with the editor, save the design JSON, and render the exported HTML into a Mailable via $this->html. Get that round-trip (open → edit → save → send → re-open and see your edits) working before adding merge tags or multiple builder modes.

Implementation Patterns

Persist both representations, render from one. Treat the design JSON as the source of truth and the HTML as a derived artifact. Regenerate HTML on save rather than trusting a stale column:

public function fields(Request $request)
{
    return [
        NovaUnlayerField::make('Design', 'design')
            ->displayMode('email')
            ->mergeTags(['first_name', 'company'])
            ->hideFromIndex(),
        // Keep the rendered output visible but read-only in Nova
        Textarea::make('HTML', 'html')->readonly()->onlyOnDetail()->rows(15),
    ];
}

If the field emits an exported-HTML payload alongside the design, use ->fillUsing() or an Email::saving() hook to copy it across instead of duplicating the editor.

Send from the rendered HTML. In the Mailable, bypass Blade templating and hand the string straight to the mailer:

public function build()
{
    return $this->html($this->email->html)
        ->subject(Str::of($this->email->subject)->replaceMatches('/{{\s*(\w+)\s*}}/', ...));
}

Two builders on one resource. Landing pages and transactional emails have different Unlayer displayMode values and different available blocks. Model them as separate fields and separate columns rather than a mode column, because the JSON shape diverges:

NovaUnlayerField::make('Page Design', 'page_design')->displayMode('web'),
NovaUnlayerField::make('Email Design', 'email_design')->displayMode('email'),

Merge tags are a two-sided contract. Defining ->mergeTags([...]) in Nova only controls the insert menu inside Unlayer; your renderer must substitute the same tokens at send time. Keep the token list in one place — a config array imported by both the Nova resource and the mailable — or they will drift silently.

Draft/publish workflow. Because the design is just JSON, cheap versioning falls out naturally: snapshot design + html into an email_versions table on publish, and let editors keep working against the live row. No editor-side support needed.

Pair with a preview route. Nova can't render the editor on index/detail, so add a signed admin route that returns the stored html for a quick "view as sent" check. This becomes the fastest debugging tool you have.

Conditional exposure. Use ->onlyOnForms() on the editor and ->onlyOnDetail() on the HTML/preview field. Rendering the Unlayer iframe on the index page for 50 rows will hammer the Nova admin.

Gotchas and Tips

  • The Unlayer embed script is remote. The editor iframe loads from editor.unlayer.com. In locked-down networks, behind a strict CSP, or in an air-gapped staging environment, the field renders blank with no obvious error. Check the browser console first; if it's CSP, you need script-src/frame-src allowances for *.unlayer.com.
  • JSON column size and packet limits. Real email designs with base64-inlined images blow past MySQL's default max_allowed_packet and can exceed TEXT (64 KB). Use JSON / longText, and prefer image URLs over uploads — Unlayer's uploader may inline data URIs into the design.
  • Silent no-op saves. Nova only submits what the form serializes. If the editor never initialized (JS error, blocked script), the field posts its original value — or null — and you'll see "saved successfully" with no changes. Alert on empty design saves during rollout.
  • Stale assets after a Nova upgrade. If the editor renders but is unformatted or missing controls, the package's compiled JS is out of sync with your Nova version. Re-publish the package assets (php artisan vendor:publish --provider="IdF\NovaUnlayerField\...") and npm run dev on the Nova side.
  • hideFromIndex() is not optional. The iframe is heavy and will noticeably degrade long index tables. Same for preview() values that try to strip_tags a JSON blob.
  • Don't ->rules('json') the design column unless you've confirmed the field posts a string rather than an already-decoded array — Nova's casting and the field's serialization can disagree, producing validation failures that only show up on certain Unlayer block types.
  • Extension point: raw Unlayer options. Most of the editor's behavior (fonts, custom blocks, tools, image upload handler) lives in Unlayer's own init() config, not in the field's fluent API. Look for a pass-through method accepting an array, and send it via ->withUnlayerOptions([...]) or the equivalent — this is where you'll spend most customization time.
  • Testing is limited to the seam. There's no headless Unlayer, so don't try to test the editor. Test the boundaries: that the field registers on the resource, that design JSON round-trips through your model cast, that the saved HTML ends up in the mailable's body, and that merge-tag substitution handles missing keys without throwing.
  • Vendor lock-in is real. Designs are stored in Unlayer's schema, and some features (custom blocks, advanced fonts, branded templates) require a paid Unlayer plan. Keep the exported html column and consider a nightly export so you can always send mail even if the editor integration is unavailable.
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
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect