inspheric/nova-email-field
A Laravel Nova field for displaying and editing email addresses with built-in validation, clickable mailto links, and a clean interface. A simple drop-in upgrade for the default email field that improves UX in forms and index views.
Install and confirm what you actually got. There is no config to publish, no migration, and no schema change — the underlying column stays a string.
composer require inspheric/nova-email-field
Before wiring it into a resource, read three files in the vendor directory (it takes two minutes and answers 90% of your questions):
vendor/inspheric/nova-email-field/composer.json — the Nova, Laravel, and PHP constraints. This is the fastest way to catch a mismatch, because the last release is v2.0.1 (2022-11-28) and predates current Nova majors.vendor/inspheric/nova-email-field/src/Email.php — which Nova field class it extends and which meta keys drive index vs. detail rendering. This is your debugging reference for the rest of the project's life.vendor/inspheric/nova-email-field/README.md plus the GitHub releases — confirm you are on the 2.x line and whether v2.0.1 is what Composer resolved.Pin the version line you land on (composer require inspheric/nova-email-field:^2.0) so an unattended composer update cannot silently move you.
First use case: swap Text::make('Email'). It is a drop-in replacement — the same modifiers work, and validation is your responsibility:
use Inspheric\EmailField\Email;
public function fields(Request $request)
{
return [
Email::make('Email', 'email')
->rules('required', 'email', 'max:255')
->sortable()
->searchable(),
];
}
On index and detail views the value renders as a mailto: link (with copy-to-clipboard support); on forms it is a plain text input. That asymmetry is the whole point of the package — everything else is standard Nova.
Treat it strictly as a presentation layer. The field renders a mailto: link; it does not validate, normalize, or verify anything. Keep your rules explicit and keep domain logic out of the field:
Email::make('Email')
->rules('required', 'email:rfc,dns', 'max:255');
If you need deliverability, MX checks, or verification workflows, those belong in a form request, a job, or a dedicated service — not here.
Display-only in listings, editable where it matters. The most common day-to-day pattern is read-only on index/detail and editable only on forms:
Email::make('Email')
->rules('required', 'email')
->sortable()
->searchable()
->hideWhenCreating()
->hideWhenUpdating();
And the inverse, when an address should be captured once at creation and never touched again:
Email::make('Email')
->rules('required', 'email')
->hideFromIndex()
->hideFromDetail();
Reuse the standard Nova modifiers rather than writing custom code. The package adds nothing here, so all the usual affordances apply:
Email::make('Email')
->onlyOnDetail()
->canSee(fn ($request) => $request->user()->isAdmin())
->help('Primary contact address — changes are logged.');
That canSee pattern is worth reaching for early on PII-adjacent resources: an email column visible to every Nova user is usually a review finding waiting to happen.
Rollout across many resources is a find-and-replace — with a catch. Swapping Text::make('Email') for Email::make('Email') is mechanical, but every field's chained modifiers live on that line, so diff each one. Grep is your friend:
grep -rn "Text::make(.*[Ee]mail" app/Nova/
Custom rendering: prefer Nova's APIs over editing vendor code. displayUsing() is a clean escape hatch for formatting or a subject line:
Email::make('Email')
->displayUsing(fn ($value) => $value ? "mailto:{$value}?subject=Account%20Enquiry" : null);
Keep in mind that a heavy displayUsing() callback on the index view runs per row — fine for a few hundred records, worth watching on large tables.
Index click behaviour is the thing to design around. Since v2.0.1 the mailto link calls stopPropagation, so clicking the address opens the mail client without also navigating to the record. That is deliberate and generally what you want, but if your resource relies on "click anywhere in the row to open," operators will now need to click a non-email cell. Document it for the ops team, and check any custom row-level JS.
Outside a resource, verify before depending on it. Action fields and tool forms can differ from resource rendering. Read src/Email.php and test in staging rather than assuming parity.
v2.0.1 changed index click behaviour. The mailto link now stops propagation. Any QA script, Playwright test, or custom JS listener that relied on clicking the email cell to open the record will break — click elsewhere in the row instead.php artisan nova:publish and rebuild assets. A stale bundle makes the upgrade look like it did nothing — this is the single most likely "it's broken" false alarm.mailto: link validates nothing. ->rules('email') or a form request is still required, and email:rfc,dns is a better default than bare email if the address matters.mailto: links depend on client configuration. On a machine with no default mail handler, the click does nothing — and because propagation is stopped, it goes nowhere at all rather than falling through to the row. Keep the address visible so it can be copied manually, and don't remove the copy affordance.sortable() and searchable() are your database's job. The field adds no query behaviour. They work only when the column exists and is queryable; on a computed or JSON-extracted attribute they will misbehave.mailto: links. Fall back to displayUsing() or write a small dedicated field.displayUsing(); otherwise your next composer install wipes the fix and you will not remember why.src/Email.php first. Confirm the parent Nova field and the meta keys controlling index vs. detail output, then compare against the release notes for your installed version. A five-minute read beats an afternoon of guessing.Text field with displayUsing(fn ($v) => $v ? "mailto:{$v}" : null) covers the core case with zero third-party dependency; you would only need to add your own @click.stop in a tiny custom component to match the v2.0.1 index behaviour. Given the package's age and adoption, treat this as prior art or a fork candidate — if it enters your stack, give it a named owner and a line on the Nova upgrade checklist.How can I help you explore Laravel packages today?