Standalone form system for Laravel Livewire. Includes input fields, layout components, relationship fields, repeaters, validation, and save lifecycle hooks. Can be used independently or together with Wire Table.
- PHP 8.2+
- Laravel 10, 11, or 12
- Livewire 3.x
- Tailwind CSS 3.x
- Node.js & npm (for Vite asset compilation)
composer require nyoncode/wire-formsThis automatically installs wire-core as a dependency. The service providers are auto-discovered.
Wire Forms uses Tailwind utility classes in its Blade templates. Add the package views to your Tailwind content paths:
Tailwind 3 (tailwind.config.js):
exportdefault{content: ['./resources/**/*.blade.php','./app/**/*.php','./vendor/nyoncode/wire-core/resources/views/**/*.blade.php','./vendor/nyoncode/wire-forms/resources/views/**/*.blade.php',],darkMode: 'class',plugins: [require('@tailwindcss/forms')],}Tailwind 4 (resources/css/app.css):
@import"tailwindcss";
@plugin"@tailwindcss/forms";
@source"../../vendor/nyoncode/wire-core/resources/views";
@source"../../vendor/nyoncode/wire-forms/resources/views";Install the forms plugin and rebuild:
npm install -D @tailwindcss/forms
npm run buildYour layout must include Vite assets, Livewire (which provides Alpine.js), and @wireStackScripts:
<!DOCTYPE html>
<htmllang="en">
<head>
@vite(['resources/css/app.css', 'resources/js/app.js'])
@livewireStyles@wireStackScripts
</head>
<body>
{{$slot}}@livewireScripts
</body>
</html>@wireStackScripts emits every installed Wire package's Alpine controllers into the initial
document — the placement that survives a wire:navigate visit and the cached Back/Forward
path. It is additive: each field still loads its own bundle as a fallback. See
JavaScript Assets.
php artisan vendor:publish --tag=wire-forms-configWire Forms works without Wire Table. Here's a complete standalone form using WithForms trait and Form class:
<?phpnamespaceApp\Livewire;
useLivewire\Component;
useNyonCode\WireForms\Components\TextInput;
useNyonCode\WireForms\Components\Select;
useNyonCode\WireForms\Components\Toggle;
useNyonCode\WireForms\Components\Layout\Section;
useNyonCode\WireForms\Forms\Form;
useNyonCode\WireForms\Forms\WithForms;
class CreateUser extends Component
{
use WithForms;
public ?array$data = [];
publicfunctionform(Form$form): Form
{
return$form
->statePath('data')
->model(User::class)
->schema([
Section::make('User Details')->schema([
TextInput::make('name')
->label('Full Name')
->required(),
TextInput::make('email')
->label('Email')
->email()
->required(),
Select::make('role')
->options([
'admin' => 'Administrator',
'editor' => 'Editor',
'viewer' => 'Viewer',
])
->required(),
Toggle::make('active')
->label('Active')
->default(true),
]),
])
->successMessage('User created');
}
publicfunctionrender()
{
returnview('livewire.create-user');
}
}<formwire:submit="$this->form->save">
{{$this->form}}
<buttontype="submit">Create User</button>
</form>Multiple forms in a single component — methods ending with Form are auto-detected:
class UserSettings extends Component
{
use WithForms;
public ?array$profileData = [];
public ?array$passwordData = [];
publicfunctionprofileForm(Form$form): Form
{
return$form
->statePath('profileData')
->model($this->user)
->schema([
TextInput::make('name')->required(),
TextInput::make('email')->email()->required(),
])
->successMessage('Profile updated');
}
publicfunctionpasswordForm(Form$form): Form
{
return$form
->statePath('passwordData')
->model($this->user)
->schema([
TextInput::make('password')->password()->confirmed(),
TextInput::make('password_confirmation')->password(),
])
->successMessage('Password changed');
}
}<formwire:submit="$this->profileForm->save">
{{$this->profileForm}}
<buttontype="submit">Save Profile</button>
</form>
<formwire:submit="$this->passwordForm->save">
{{$this->passwordForm}}
<buttontype="submit">Change Password</button>
</form>Forms work without Livewire — useful for testing and console commands:
useNyonCode\WireForms\Forms\Form;
useNyonCode\WireForms\Components\TextInput;
$form = Form::make()
->schema([
TextInput::make('name')->required(),
TextInput::make('email')->email()->required(),
])
->state(['name' => 'John', 'email' => 'john@example.com']);
$data = $form->validate(); // throws ValidationException on failureWhen used with Wire Table, form fields are rendered inside action modals:
useNyonCode\WireCore\Actions\Action;
useNyonCode\WireForms\Components\TextInput;
useNyonCode\WireForms\Components\Select;
Action::make('edit')
->form([
TextInput::make('name')->required(),
Select::make('status')->options([...]),
])
->action(function ($record, $data) {
$record->update($data);
});See Wire Table documentation for details.
| Field | Description |
|---|---|
TextInput | Text, email, number, tel, url, password |
Textarea | Multi-line text |
Select | Dropdown with options |
Checkbox | Single checkbox |
CheckboxList | Multiple checkboxes |
Radio | Radio button group |
Toggle | Toggle switch |
DateTimePicker | Date, time, or datetime picker (->mode('date'|'time'|'datetime')) |
ColorPicker | Color picker |
FileUpload | File upload with preview |
RichEditor | Rich text editor |
Hidden | Hidden input |
BelongsToSelect | Select field backed by a belongsTo relation |
MorphToSelect | Select fields for polymorphic relations |
Repeater | Repeatable nested form rows |
| Component | Description |
|---|---|
Section | Collapsible section with heading |
Fieldset | Grouped fields with legend |
Grid | Multi-column grid layout |
| Component | Description |
|---|---|
Placeholder | Static text display |
Alert | Alert/callout box |
Html | Raw HTML content |
ViewField | Custom Blade view |
TextInput::make('name')
->label('Custom Label')
->placeholder('Enter value...')
->default('Default value')
->required()
->disabled()
->readonly()
->hidden()
->helperText('Help text below the field')
->hint('Hint text', 'info-icon')
->prefix('$')
->suffix('.00')
->prefixIcon('currency')
->suffixIcon('calculator')
->columnSpan(2)
->columnSpanFull()
->rules(['min:3', 'max:255'])
->extraAttributes(['data-testid' => 'name-input']);$form
->schema(array $components)
->statePath(string $path)
->fill(array $data)
->state(array $data) // alias for fill()
->getState(): array
->validate(): array // throws ValidationException
->model(string|Model|null $model)
->save(): ?Model
->using(Closure $fn) // override Eloquent persistence
->mutateDataBeforeSave(Closure $fn)
->beforeSave(Closure $fn)
->afterSave(Closure $fn)
->successMessage(string|Closure|null $message)
->disableSuccessNotification()
->authorize()
->authorizeUsing(Closure $fn)
->canSave(): bool
->disabled(bool $disabled = true)
->isCreating(): bool
->isEditing(): bool
->getModel(): Model|string|null
->getFlatComponents(): array
->getValidationRules(): array;php artisan vendor:publish --tag=wire-forms-config| Document | Description |
|---|---|
| Forms Overview | Form setup, standalone usage, and save flow |
| Field Reference | Built-in field components |
| Validation | Rules, messages, and validation behavior |
| Save Lifecycle | Mutation, persistence, hooks, and notifications |
| Authorization | Policy and callback authorization |
| Configuration | Date formats, uploads, and rich editor config |
MIT