Skip to content

Latest commit

History

29 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Wire Forms

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.

Requirements

  • PHP 8.2+
  • Laravel 10, 11, or 12
  • Livewire 3.x
  • Tailwind CSS 3.x
  • Node.js & npm (for Vite asset compilation)

Installation

composer require nyoncode/wire-forms

This automatically installs wire-core as a dependency. The service providers are auto-discovered.

Tailwind CSS Setup

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 build

Layout Template

Your 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.

Publish Config (optional)

php artisan vendor:publish --tag=wire-forms-config

Quick Start: Standalone Form

Wire 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>

Multi-Form Example

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>

Standalone Usage (Testing / Jobs)

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 failure

Quick Start: In Action Modal (with Wire Table)

When 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 Types

Input Fields

FieldDescription
TextInputText, email, number, tel, url, password
TextareaMulti-line text
SelectDropdown with options
CheckboxSingle checkbox
CheckboxListMultiple checkboxes
RadioRadio button group
ToggleToggle switch
DateTimePickerDate, time, or datetime picker (->mode('date'|'time'|'datetime'))
ColorPickerColor picker
FileUploadFile upload with preview
RichEditorRich text editor
HiddenHidden input
BelongsToSelectSelect field backed by a belongsTo relation
MorphToSelectSelect fields for polymorphic relations
RepeaterRepeatable nested form rows

Layout Components

ComponentDescription
SectionCollapsible section with heading
FieldsetGrouped fields with legend
GridMulti-column grid layout

Display Components

ComponentDescription
PlaceholderStatic text display
AlertAlert/callout box
HtmlRaw HTML content
ViewFieldCustom Blade view

Common Field API

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 API

$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;

Configuration

php artisan vendor:publish --tag=wire-forms-config

Documentation

DocumentDescription
Forms OverviewForm setup, standalone usage, and save flow
Field ReferenceBuilt-in field components
ValidationRules, messages, and validation behavior
Save LifecycleMutation, persistence, hooks, and notifications
AuthorizationPolicy and callback authorization
ConfigurationDate formats, uploads, and rich editor config

License

MIT

About

READ ONLY

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages