K

Forms

Wire Forms

Standalone form system for Laravel Livewire. Works independently or with Wire Table.

Wire Forms preview

Standalone form system for Laravel Livewire. Works independently or with Wire Table.

Installation

composer require nyoncode/wire-forms

Add to Tailwind content paths:

export default {
content: [
// ...current app paths
'./vendor/nyoncode/wire-core/resources/views/**/*.blade.php',
'./vendor/nyoncode/wire-forms/resources/views/**/*.blade.php',
]
}

How Forms Work

Define a Form schema on your Livewire component, bind it to a state path, and render it with {{ $this->form }}.


Single Form

use NyonCode\WireForms\Forms\Form;
use NyonCode\WireForms\Forms\WithForms;
use NyonCode\WireForms\Components\TextInput;
 
class CreateUser extends Component
{
use WithForms;
 
public ?array $data = [];
 
public function form(Form $form): Form
{
return $form
->statePath('data')
->model(User::class)
->schema([
TextInput::make('name')->required(),
TextInput::make('email')->email()->required(),
])
->successMessage('User created');
}
 
public function save(): void
{
$this->form->save();
}
}
<form wire:submit="save">
{{ $this->form }}
<button type="submit">Create</button>
</form>

Multi-Form

Methods ending with Form are auto-detected:

class UserSettings extends Component
{
use WithForms;
 
public array $profileData = [];
public array $passwordData = [];
 
public function profileForm(Form $form): Form
{
return $form
->statePath('profileData')
->model($this->user)
->schema([
TextInput::make('name')->required(),
TextInput::make('bio'),
]);
}
 
public function passwordForm(Form $form): Form
{
return $form
->statePath('passwordData')
->schema([
TextInput::make('current_password')->password()->required(),
TextInput::make('password')->password()->required()->confirmed(),
TextInput::make('password_confirmation')->password()->required(),
]);
}
 
public function saveProfile(): void
{
$this->profileForm->save();
}
 
public function savePassword(): void
{
$data = $this->passwordForm->validate();
$this->user->update(['password' => Hash::make($data['password'])]);
}
}
<form wire:submit="saveProfile">
{{ $this->profileForm }}
<button type="submit">Save Profile</button>
</form>
 
<form wire:submit="savePassword">
{{ $this->passwordForm }}
<button type="submit">Change Password</button>
</form>

Explicit Form Registration

Alternative to auto-detection — return method names exactly as defined:

protected function getForms(): array
{
return ['profileForm', 'passwordForm'];
}

Model Modes

// Create mode — Form::save() calls User::create($data)
$form->model(User::class);
 
// Edit mode — Form::save() calls $user->update($data)
$form->model($user);
 
// No model — save() throws, but validate() works
$form->model(null);

Introspection:

$form->isCreating(); // true when model is a class string
$form->isEditing(); // true when model is an instance
$form->getModel(); // Model instance or null

Standalone Usage (without Livewire)

Works for server-side validation and data processing:

$form = Form::make()
->schema([
TextInput::make('name')->required()->maxLength(255),
TextInput::make('email')->email()->required(),
])
->state(['name' => 'John', 'email' => 'john@example.com']);
 
// Validate only
$validated = $form->validate(); // throws ValidationException on failure
 
// Validate + save
$form->model(User::class)->save();

Form API Reference

Schema & State

->schema(array $components) // field definitions
->statePath(string $path) // Livewire property name for state
->fill(array $data) // populate state
->state(array $data) // alias for fill()
->getState(): array // current state
->getValidationRules(): array // collected rules
->validate(): array // validate and return data

Model & Save

->model(string|Model|null $model) // Eloquent model (class for create, instance for edit)
->save(): mixed // full save lifecycle
->using(Closure $fn) // custom save callback (replaces default persist)

Save Lifecycle Hooks

->mutateDataBeforeSave(Closure $fn) // fn(array $data): array — transform data before persist
->beforeSave(Closure $fn) // fn(array $data): void — runs before persist
->afterSave(Closure $fn) // fn(Model|mixed $record): void — runs after persist

Notifications

->successMessage(string|Closure|null $msg) // custom success notification text; Closure receives $record
->disableSuccessNotification() // no notification after save

Validation

->validationMessages(array $msgs) // custom validation messages

State

->disabled(bool $disabled = true) // make all fields read-only

Authorization

->authorize(bool $usePolicy = true) // enable model policy auto-resolution (create/update)
->authorizeUsing(?Closure $callback) // fn(User $user): bool — custom auth check
->canSave(): bool // whether the current user may save
->isReadOnly(): bool // true when authorization denies save

When ->authorize() is enabled the form becomes read-only (and hides the save button) if the current user lacks the create or update policy permission on the model.

Introspection

->isCreating(): bool // model is class string
->isEditing(): bool // model is instance
->getModel(): ?Model // current model instance
->getFlatComponents(): array // all components (flat)

Rendering

->toHtml(): string // Blade output
(string) $form // __toString()

Factory

Form::make() // static factory via container

Livewire Binding

->livewire(Component $component) // bind to Livewire component

WithForms Trait

The WithForms trait provides:

  1. Auto-detection — scans for methods ending in Form and registers them
  2. Lazy resolution — forms are only built when first accessed
  3. Caching — form instances are cached for the request lifecycle
  4. Magic property access$this->profileForm resolves the form
class MyComponent extends Component
{
use WithForms;
 
// Access via:
// $this->form → calls form() method
// $this->profileForm → calls profileForm() method
// $this->settingsForm → calls settingsForm() method
}

Field Types

Input Fields

Layout Components

  • Grid — CSS grid layout
  • Section — collapsible section with heading
  • Fieldset — HTML fieldset

Display Components

Shared Field API

Every field inherits:

->label(string|Closure $label)
->helperText(string|Closure $text)
->hint(string|Closure $hint)
->hintIcon(string $icon)
->required(bool|Closure $required = true)
->hidden(bool|Closure $hidden = true)
->visible(bool|Closure $visible = true)
->disabled(bool|Closure $disabled = true)
->size('sm'|'md'|'lg'|'xl')
->columnSpan(int|string $span) // grid column span
->columnStart(int $start) // grid column start
->default(mixed $value) // default value
->extraAttributes(array $attrs) // HTML attributes
->live() // wire:model.live
->debounce(int $ms = 500) // wire:model.blur with debounce
->rules(string|array $rules) // Laravel validation rules
->validationMessages(array $messages) // custom validation messages