A standalone, open-source Laravel Livewire package that provides a dynamic, drag-and-drop Form Builder. It lets you visually compose forms, exports the result as form schema JSON, and renders saved schemas as functional forms.
Inspired by the schema layout and field options of jQuery FormBuilder — but built natively with Livewire 4 + Alpine.js. No jQuery, no FormBuilder JS is used.
wire:sortable reordering.className).#[Modelable] binding on both components so a parent can stay
in sync with wire:model — no manual listeners required.config/wire-form-builder.php.See the form builder in action:
This package ships its styles as Tailwind utility classes through its Blade
views and config/wire-form-builder.php. You must make sure your app's
Tailwind build scans those files, otherwise classes (e.g. the builder's
grid-cols-[16rem_1fr]) are never generated and the palette/canvas will
appear unstyled and stacked.
Tailwind v4 does not scan vendor/ automatically. Add the package's
views (and optionally its config) as a source in your resources/css/app.css:
@import "tailwindcss";
@source "../../vendor/mrclln/wire-form-builder/resources/views";
@source "../../vendor/mrclln/wire-form-builder/config";
Then rebuild your assets and hard-refresh:
npm run build
Include the package in your content globs in tailwind.config.js:
export default {
content: [
'./resources/**/*.blade.php',
'./vendor/mrclln/wire-form-builder/**/*.blade.php',
'./vendor/mrclln/wire-form-builder/config/**/*.php',
],
};
composer require mrclln/wire-form-builder
Publish the configuration and/or the views:
php artisan vendor:publish --tag=wire-form-builder-config
php artisan vendor:publish --tag=wire-form-builder-views
Drop the editor anywhere in a Blade view:
<livewire:wire-form-builder.form-builder />
Add fields from the sidebar, reorder them by dragging the handle, edit each field in the drawer (label, name, placeholder, required, options, or a custom CSS class), duplicate a field, or remove it. The generated JSON is kept in sync with the canvas at all times.
You can also seed an existing schema:
<livewire:wire-form-builder.form-builder
:initial-fields='[{"type":"text","name":"full_name","label":"Full Name","required":true}]' />
Or pre-seed from a JSON schema string:
<livewire:wire-form-builder.form-builder :schema='$form->schema' />
#[Modelable])The schema property is marked #[Modelable], so a parent component can bind
to it with wire:model and keep it in two-way sync as fields are added, edited,
reordered, duplicated, or removed:
<livewire:wire-form-builder.form-builder wire:model="formSchema" />
Whenever the canvas changes, $formSchema on the parent updates automatically
(no manual @input listener required). To react to changes server-side, emit a
listener or read the property in any parent action.
You can also read or normalise the canvas from plain PHP via the component's helpers:
// Schema JSON string -> array of field definitions.
$array = $formBuilder->parseSchema($form->schema);
// Pretty-printed JSON schema (internal ids stripped).
$json = $formBuilder->getSchemaJson();
Pass a schema (array or JSON string) to render a functional form:
<livewire:wire-form-builder.form-renderer :schema='$form->schema' />
The schemaFields property is marked #[Modelable], so you can also bind it
two-way with wire:model. As users fill in the rendered fields, the value
key is written into each schema entry, and the parent's bound property updates
in real-time — no submit button required.
<livewire:wire-form-builder.form-renderer wire:model="formSchema" />
The rendered form fields bind to schemaFields.{index}.value, so every keystroke
is reflected in the parent's $formSchema array. Each entry looks like:
[
'name' => 'text_field',
'label' => 'Text Field',
'type' => 'text',
'value' => 'user typed value',
],
Retrieve a clean name => value map at any time via $formRenderer->getAnswers().
When you need to read, normalise, or persist schema JSON from plain PHP (e.g.
saving a builder's canvas to the database), use the FormBuilder facade:
use FormBuilder;
// JSON string or array -> list of field definitions (ids normalised).
$fields = FormBuilder::fieldsFromSchema($form->schema);
// List of field definitions -> pretty-printed JSON schema (ids stripped).
$json = FormBuilder::schemaFromFields($fields);
// Parse a schema (JSON string or array) into an array of field definitions.
$array = FormBuilder::parseSchema($form->schema);
// Alias of fieldsFromSchema() — returns the schema as a plain PHP array.
$array = FormBuilder::toArray($form->schema);
Every class is defined in config/wire-form-builder.php under styles. For
example, to restyle text inputs:
'controls' => [
'text' => [
'label' => 'block text-sm font-semibold text-gray-900 mb-1',
'input' => 'w-full rounded-lg border-gray-400 ...',
],
],
Views reference these via config('wire-form-builder.styles...'), so no
Blade editing is required for restyling. The field_types key controls which
field types appear in the palette.
| Class | Responsibility |
|---|---|
LivewireFormBuilderServiceProvider |
Registers components, views, publishes config, binds the facade. |
Components\FormBuilder |
The editor: palette, sortable canvas, settings drawer, duplicate/remove, JSON schema. |
Components\FormRenderer |
Renders a saved schema into a functional form using #[Modelable] two-way binding on schemaFields. |
FieldDefinitions |
Source of truth for field types and their default schema. |
FormBuilder (facade) / FormBuilderManager |
Backend API to read, normalise, and persist schema JSON from PHP. |
config/wire-form-builder.php |
Publishable styles + enabled field types. |
resources/views/fields/*.blade.php |
Per-control rendering partials. |
MIT © mrclln
How can I help you explore Laravel packages today?