| name | aire-development |
| description | Build and work with Aire forms in Laravel, including the fluent PHP API, data binding, validation, and customization. |
Aire Form Development
When to use this skill
Use this skill when creating, modifying, debugging, or styling HTML forms in a Laravel application that uses the glhd/aire package. This includes any work involving form elements, data binding, validation, or form customization.
Overview
Aire is a Laravel form builder with a fluent API. It is accessed via the Aire facade (Galahad\Aire\Support\Facades\Aire). Aire automatically handles CSRF tokens, HTTP method spoofing, old input repopulation, and server-side validation error display.
Opening and Closing Forms
Every form must be opened and closed. Aire uses output buffering between open and close to capture form fields.
{{ Aire::open()->route('users.store') }}
{{-- form fields --}}
{{ Aire::close() }}
Setting the Action
Aire::open()->route('users.store')
Aire::open()->route('users.update', $user)
Aire::open()->action('/users')
Aire::open()->resourceful($user)
Aire::open()->resourceful($user, 'admin.users')
HTTP Methods
Aire automatically infers the method from the route. You can also set it explicitly:
Aire::open()->route('users.store')
Aire::open()->route('users.update', $user)
Aire::open()->post()
Aire::open()->put()
Aire::open()->patch()
Aire::open()->delete()
For PUT, PATCH, and DELETE, Aire automatically adds a hidden _method field and sets the real method to POST.
Form Encoding
Aire::open()->multipart()
Aire::open()->urlEncoded()
Data Binding
Bind an Eloquent model, array, or object to auto-populate form fields:
{{ Aire::open()->route('users.update', $user)->bind($user) }}
{{ Aire::input('name', 'Name') }} {{-- pre-filled with $user->name --}}
{{ Aire::close() }}
Or with resourceful (which calls bind internally):
{{ Aire::open()->resourceful($user) }}
Value precedence (highest to lowest):
- Explicitly set via
->value()
- Old input from session (after validation failure)
- Bound data from the model/array/object
Available Form Elements
Text Inputs
Aire::input('name', 'Full Name')
Aire::email('email', 'Email Address')
Aire::password('password', 'Password')
Aire::search('q', 'Search')
Aire::tel('phone', 'Phone')
Aire::url('website', 'Website')
Aire::number('age', 'Age')
Aire::range('rating', 'Rating', 1, 10)
Date and Time Inputs
Aire::date('start_date', 'Start Date')
Aire::dateTime('event_at', 'Event Date/Time')
Aire::dateTimeLocal('local_at', 'Local Date/Time')
Aire::time('start_time', 'Start Time')
Aire::month('birth_month', 'Birth Month')
Aire::week('target_week', 'Target Week')
Other Inputs
Aire::color('theme_color', 'Theme Color')
Aire::file('avatar', 'Avatar')
Aire::hidden('user_id', $user->id)
Textarea
Aire::textArea('bio', 'Biography')
Select
Aire::select(['draft' => 'Draft', 'published' => 'Published'], 'status', 'Status')
Aire::timezoneSelect('timezone', 'Timezone')
Checkboxes
Aire::checkbox('terms', 'I agree to the terms')
Aire::checkboxGroup(['red' => 'Red', 'blue' => 'Blue', 'green' => 'Green'], 'colors', 'Favorite Colors')
Radio Buttons
Aire::radioGroup(['sm' => 'Small', 'md' => 'Medium', 'lg' => 'Large'], 'size', 'Size')
Buttons
Aire::submit('Save')
Aire::button('Cancel')
Error Summary
Display a summary of all validation errors at the top of the form:
Aire::summary()
Aire::summary()->verbose()
Fluent Element Methods
All elements support chaining. Common methods available on every input element:
Aire::input('name', 'Name')
->id('custom-id')
->value('default')
->required()
->disabled()
->readOnly()
->placeholder('Enter name')
->autoComplete('name')
->autoFocus()
->addClass('custom-class')
->removeClass('old-class')
->data('key', 'value')
->variant('lg')
->variants('lg primary')
Grouping (Labels, Help Text, Errors)
By default, every form element is wrapped in a "group" that renders a label, the input, help text, and validation errors together. This is controlled by the group_by_default config option.
Aire::input('name')
->label('Full Name')
->helpText('Enter your legal name')
->withoutGroup()
->grouped()
->groupClass('mb-4')
->groupId('name-group')
->prepend('$')
->append('.00')
Validation
Server-Side Validation Errors
Aire automatically reads errors from the session (set by Laravel's validate() or FormRequest) and displays them inline within each element's group. No extra configuration needed.
Using a Custom Error Bag
Aire::open()->errorBag('login')
Client-Side Validation
Aire includes optional JavaScript-based client-side validation using the validatorjs library:
Aire::open()
->route('users.store')
->validate([
'name' => 'required|min:3',
'email' => 'required|email',
])
Aire::open()
->route('users.store')
->validate(StoreUserRequest::class)
Aire::open()->withoutValidation()
Alpine.js Integration
Aire can generate x-data and x-model attributes for Alpine.js:
Aire::open()->asAlpineComponent()
Aire::open()->asAlpineComponent(['extra_key' => 'value'])
When enabled, each input element will get an x-model attribute matching its name, and the form gets an x-data attribute with JSON-serialized initial values.
Configuration
Publish the config file to customize Aire's behavior:
php artisan vendor:publish --tag=aire-config
This publishes config/aire.php where you can set:
default_classes: CSS classes for each element type
variant_classes: Named style variants (e.g., sm, lg, primary)
validation_classes: CSS classes for validation states (none, valid, invalid)
group_by_default: Whether elements are grouped by default
validate_by_default: Whether client-side validation is on by default
You can also publish and override Blade templates:
php artisan vendor:publish --tag=aire-views
Complete Form Examples
Create Form
{{ Aire::open()->route('posts.store') }}
{{ Aire::summary() }}
{{ Aire::input('title', 'Title')->required() }}
{{ Aire::textArea('body', 'Content')->autoSize() }}
{{ Aire::select(['draft' => 'Draft', 'published' => 'Published'], 'status', 'Status') }}
{{ Aire::submit('Create Post') }}
{{ Aire::close() }}
Edit Form with Model Binding
{{ Aire::open()->resourceful($post) }}
{{ Aire::summary() }}
{{ Aire::input('title', 'Title')->required() }}
{{ Aire::textArea('body', 'Content')->autoSize() }}
{{ Aire::select(['draft' => 'Draft', 'published' => 'Published'], 'status', 'Status') }}
{{ Aire::submit('Update Post') }}
{{ Aire::close() }}
Delete Form
{{ Aire::open()->route('posts.destroy', $post) }}
{{ Aire::submit('Delete Post')->variant('danger') }}
{{ Aire::close() }}
Form with Client-Side Validation
{{ Aire::open()->route('users.store')->validate(StoreUserRequest::class) }}
{{ Aire::summary() }}
{{ Aire::input('name', 'Name')->required() }}
{{ Aire::email('email', 'Email')->required() }}
{{ Aire::password('password', 'Password')->required() }}
{{ Aire::password('password_confirmation', 'Confirm Password')->required() }}
{{ Aire::submit('Register') }}
{{ Aire::close() }}
File Upload Form
{{ Aire::open()->route('avatars.store')->multipart() }}
{{ Aire::file('avatar', 'Profile Photo') }}
{{ Aire::submit('Upload') }}
{{ Aire::close() }}