eom-plus/nova-form-designer
Nova Form Designer lets you build and manage dynamic forms inside Laravel Nova. Define fields and layouts visually, save form schemas, and reuse them across your application for flexible admin-driven form creation.
Install and register the package
composer require eom-plus/nova-form-designer
Then publish any config/assets it ships with:
php artisan vendor:publish --provider="EomPlus\NovaFormDesigner\NovaFormDesignerServiceProvider"
php artisan nova:publish
The package is Nova‑4 only, so confirm your laravel/nova constraint is ^4.0 before installing; it will not work on Nova 3 resources.
Where to look first
config/nova-form-designer.php) — this is where the designer’s available field types, storage behaviour, and rendering options are usually toggled.vendor/eom-plus/nova-form-designer/src. Identify whether the package exposes a Nova Field (FormDesigner) or a Nova Tool (registered in NovaServiceProvider::tools()), because that determines the whole integration style.First use case
The fastest win is a single resource with a json column:
// migration
$table->json('form_schema')->nullable();
// model
protected $casts = ['form_schema' => 'array'];
// Nova resource
use EomPlus\NovaFormDesigner\FormDesigner;
public function fields(Request $request)
{
return [
FormDesigner::make('Form Schema', 'form_schema'),
];
}
Open the resource, drag fields into the designer, save. You now have a persisted schema you can render on the frontend. Start here before attempting custom rendering or standalone form routes.
Pattern 1 — Schema as a resource attribute
Keep the designer attached to a model (e.g. Form, Survey, Application). Treat the JSON column as the source of truth and never edit it by hand in production. This is the pattern the package is optimised for.
Pattern 2 — Designer as a separate tool, forms as records
If the package registers a Nova tool, use it to author templates, then have a Form model reference a template plus a version. This decouples editing from submissions and lets you evolve a form without invalidating historical data.
Pattern 3 — Render with a shared Blade component
Wrap rendering in one partial so Nova preview and your public routes stay consistent:
@include('nova-form-designer::render', ['schema' => $form->form_schema, 'values' => old()])
If the package does not ship a renderer, write a small recursive Blade/PHP renderer keyed off the schema’s type values — the schema is plain JSON, so this is trivial and gives you full control.
Pattern 4 — Server-side validation mirror
The visual designer is presentation only. Generate a FormRequest (or map schema → Validator rules) so validation never depends on the client:
$rules = collect($form->form_schema['fields'])
->mapWithKeys(fn ($f) => ["data.{$f['name']}" => $f['rules'] ?? ['nullable']])
->all();
Consider storing validation rules inside the schema itself so the designer becomes the single definition point.
Pattern 5 — Submissions
Keep submissions in their own table with form_id, schema_version, and a JSON payload. Because the schema can change, snapshotting the version (or the schema itself) alongside the submission is the difference between a usable audit trail and an unexplainable one.
Pattern 6 — Seeding and fixtures
Author forms once in the UI, export the schema JSON, and commit it as a seeder fixture. This makes staging/production reproducible and lets you write feature tests against a known schema.
Integration tips
FormRequest; never trust the client to omit hidden fields.Maturity risk is the headline. 15 stars and zero dependents means you are effectively the first production user. Pin the exact version ("eom-plus/nova-form-designer": "1.0.3"), vendor the source if necessary, and budget for maintaining a fork. Check the last commit date and whether the repo has tests before committing to it.
Nova asset pipeline. Nova 4 packages ship compiled JS. If the designer renders blank or the drag-and-drop does nothing, run php artisan nova:publish --force and clear public/vendor/nova caches. Check the browser console and the /nova-api network tab — a 403 there usually means the tool isn’t authorised, not that the package is broken.
JSON column vs text. Some packages expect text and json_encode themselves; others expect a native json column and an array cast. A mismatch produces double-encoded strings ("{\"fields\":...}"). Inspect the raw DB value first when rendering fails.
Schema drift on upgrade. There is unlikely to be a migration path between package versions. Treat schemas as your own data format: validate them on read, default missing keys, and version them explicitly.
Field-type names are a contract. Whatever type strings the designer emits ("text", "select", "file") are what your renderer must handle. Log unknown types rather than silently dropping them — otherwise fields vanish from rendered forms after a package update.
File uploads. If the designer includes file fields, confirm where files are stored and who authorises downloads. A designer-authored upload field can easily become an unauthenticated file endpoint if the renderer posts directly.
Debugging checklist
json_decode result — is it an array of fields or a wrapper object?type values — do they match your renderer’s cases?Extension points
vendor.$view->prependNamespace() or publish the views.The pragmatic call
Use it if a drag-and-drop authoring UI is genuinely a requirement and you accept ownership of the code. If you only need structured, configurable forms, a plain JSON schema plus a hand-written renderer will be less work than debugging an unmaintained Nova add-on — and you will understand every line.
How can I help you explore Laravel packages today?