Standalone Components
FilaForms ships two standalone Livewire components that work without the Filament admin panel or database. Use them to embed form building and rendering anywhere in your Laravel application.
Form Builder
The visual drag-and-drop form builder as a standalone Livewire component.
Basic Usage
{{-- Empty builder --}}
<livewire:filaforms::form-builder />
{{-- Pre-populated with an existing schema --}}
<livewire:filaforms::form-builder :form-schema="$existingFormJson" />
Capturing Form Data
The builder emits a form-updated Livewire event whenever the user makes changes. Listen for this event to capture the current form state:
use Livewire\Attributes\On;
use Livewire\Component;
class MyFormManager extends Component
{
public ?array $formData = null;
#[On('form-updated')]
public function onFormUpdated(array $formData): void
{
$this->formData = $formData;
}
public function save(): void
{
DB::table('my_forms')->insert([
'form_json' => json_encode($this->formData),
'created_at' => now(),
]);
}
public function render()
{
return view('my-form-manager');
}
}
<div>
<livewire:filaforms::form-builder />
<button wire:click="save">Save Form</button>
</div>
JSON Form Structure
The builder outputs a JSON structure with sections, fields, and settings:
{
"id": "01HZXF1A2B3C4D5E6F7G8H9J0K",
"name": "Customer Feedback Form",
"settings": {
"experience": {
"display_mode": "simple",
"submit_button_text": "Send Feedback",
"success_message": "Thank you!"
}
},
"schema": {
"sections": [
{
"id": "01HZXF2B3C4D5E6F7G8H9J0KL",
"code": "personal_info",
"name": "Personal Information",
"type": "section",
"active": true,
"sortOrder": 0,
"settings": {},
"fields": [
{
"id": "01HZXF3C4D5E6F7G8H9J0KLM",
"code": "full_name",
"type": "text",
"label": "Full Name",
"active": true,
"sortOrder": 0,
"width": "100",
"validationRules": [],
"settings": {},
"options": []
},
{
"id": "01HZXF4D5E6F7G8H9J0KLMN",
"code": "email",
"type": "email",
"label": "Email Address",
"active": true,
"sortOrder": 1,
"width": "100",
"validationRules": [],
"settings": {},
"options": []
}
]
}
]
}
}
The schema is structured into three top-level keys:
| Key | Purpose |
|---|---|
settings.experience | Display mode, button labels, submission behavior |
schema.sections | Ordered sections, each containing fields |
id, name | Form identification |
All experience settings use snake_case keys (e.g., display_mode, submit_button_text). See Experience Settings for the full list.
Form Renderer
Render forms from JSON or a database record without any admin panel.
From JSON Schema
<livewire:filaforms::form-renderer :form-schema="$formSchema" />
From Database Record
@php
$form = \FilaForms\Core\Models\Form::where('slug', 'contact-us')->first();
@endphp
@if($form)
<livewire:filaforms::form-renderer :form-record="$form" />
@endif
Prefilling Fields
Pass an initialData array to prefill form fields with values. Keys are field codes, values are the prefill data:
<livewire:filaforms::form-renderer
:form-record="$form"
:initial-data="[
'full_name' => $user->name,
'email' => $user->email,
'company' => $user->company_name,
]"
/>
This works with both database records and JSON schemas:
<livewire:filaforms::form-renderer
:form-schema="$formSchema"
:initial-data="['source' => 'landing-page']"
/>
Handling Submissions
The renderer emits a form-submitted event after successful submission:
use Livewire\Attributes\On;
use Livewire\Component;
class PublicFormPage extends Component
{
public string $formSchema;
#[On('form-submitted')]
public function handleSubmission(array $submissionData): void
{
DB::table('form_submissions')->insert([
'form_id' => $submissionData['form_id'],
'data' => json_encode($submissionData['fields']),
'submitted_at' => now(),
]);
session()->flash('message', 'Thank you for your submission!');
}
}
Submission Data Structure
{
"form_id": "01HZXF1A2B3C4D5E6F7G8H9J0K",
"submitted_at": "2024-01-15T10:30:00Z",
"fields": {
"01HZXF3C4D5E6F7G8H9J0KLM": "John Doe",
"01HZXF4D5E6F7G8H9J0KLMN": "john@example.com"
}
}
Experience Settings
The settings.experience object controls display mode, submission behavior, and button labels. All keys use snake_case.
| Key | Type | Default | Description |
|---|---|---|---|
display_mode | "simple" | "wizard" | "simple" | Single page or multi-step wizard |
submit_button_text | string | "Submit" | Label on the submit button |
success_message | string | Default thank-you HTML | Message shown after submission |
one_per_person | bool | false | Limit to one submission per visitor |
max_submissions | int|null | null | Maximum total submissions allowed |
allow_step_navigation | bool | true | Allow clicking previous steps in wizard mode |
show_progress_bar | bool | true | Show progress indicator in wizard mode |
show_step_numbers | bool | true | Show step numbers in wizard progress bar |
next_button_text | string | "Next" | Label on wizard next button |
previous_button_text | string | "Previous" | Label on wizard previous button |
FormExperienceData for the complete list of properties.Wizard Mode
To render a standalone form as a multi-step wizard, set display_mode to "wizard". Each section in the schema becomes a wizard step.
{
"settings": {
"experience": {
"display_mode": "wizard",
"submit_button_text": "Submit Application",
"allow_step_navigation": true,
"next_button_text": "Continue",
"previous_button_text": "Go Back"
}
},
"schema": {
"sections": [
{
"id": "sec_1",
"code": "personal_info",
"name": "Personal Information",
"description": "Tell us about yourself",
"type": "section",
"active": true,
"sortOrder": 0,
"settings": {},
"fields": []
},
{
"id": "sec_2",
"code": "experience",
"name": "Work Experience",
"description": "Your professional background",
"type": "section",
"active": true,
"sortOrder": 1,
"settings": {},
"fields": []
}
]
}
}
Each section's name becomes the step title and description becomes the step subtitle. Validation is enforced per step -- the respondent must pass all field validations on the current step before advancing.
Storage Options
You control where form JSON is stored. Common patterns:
// Database table
DB::table('my_forms')->insert([
'form_json' => json_encode($formData),
'created_by' => auth()->id(),
]);
// JSON file
Storage::put("forms/{$formId}.json", json_encode($formData));
// Cache (temporary)
Cache::put("form:{$formId}", $formData, now()->addHours(24));
Complete Example
A full workflow: admin creates a form with the builder, public visitors fill it out via the renderer.
Step 1: Admin Creates the Form
use Livewire\Attributes\On;
use Livewire\Component;
class FormCreator extends Component
{
public ?array $formData = null;
#[On('form-updated')]
public function onFormUpdated(array $formData): void
{
$this->formData = $formData;
}
public function save()
{
$id = DB::table('my_forms')->insertGetId([
'json_structure' => json_encode($this->formData),
'created_at' => now(),
]);
return redirect("/forms/{$id}/preview");
}
public function render()
{
return view('livewire.form-creator');
}
}
<div>
<livewire:filaforms::form-builder />
<button wire:click="save">Save Form</button>
</div>
Step 2: Public Visitor Fills the Form
use Livewire\Attributes\On;
use Livewire\Component;
class FormDisplay extends Component
{
public int $formId;
public string $formSchema;
public function mount(int $formId): void
{
$this->formSchema = DB::table('my_forms')
->where('id', $formId)
->value('json_structure');
}
#[On('form-submitted')]
public function onSubmitted(array $data): void
{
DB::table('my_submissions')->insert([
'form_id' => $this->formId,
'data' => json_encode($data),
'created_at' => now(),
]);
session()->flash('success', 'Thank you!');
}
public function render()
{
return view('livewire.form-display');
}
}
<div>
<livewire:filaforms::form-renderer :form-schema="$formSchema" />
</div>